Compare commits
2 Commits
f41c85b703
...
d78820dcd4
| Author | SHA1 | Date | |
|---|---|---|---|
|
d78820dcd4
|
|||
|
d43c4232a2
|
@@ -11,6 +11,7 @@ use serde::Deserialize;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
const API_BASE: &str = "https://api.anthropic.com/v1";
|
||||
const CLAUDE_CODE_PREFIX: &str = "You are Claude Code, Anthropic's official CLI for Claude.";
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct ClaudeConfig {
|
||||
@@ -94,6 +95,7 @@ async fn prepare_chat_completions(
|
||||
for (key, value) in provider.extra_request_headers() {
|
||||
request_data.header(key, value);
|
||||
}
|
||||
inject_oauth_system_prompt(&mut request_data.body);
|
||||
} else if let Ok(api_key) = self_.get_api_key() {
|
||||
request_data.header("x-api-key", api_key);
|
||||
} else {
|
||||
@@ -107,6 +109,43 @@ async fn prepare_chat_completions(
|
||||
Ok(request_data)
|
||||
}
|
||||
|
||||
/// Anthropic requires OAuth-authenticated requests to include a Claude Code
|
||||
/// system prompt prefix in order to consider a request body as "valid".
|
||||
///
|
||||
/// This behavior was discovered 2026-03-17.
|
||||
///
|
||||
/// So this function injects the Claude Code system prompt into the request
|
||||
/// body to make it a valid request.
|
||||
fn inject_oauth_system_prompt(body: &mut Value) {
|
||||
let prefix_block = json!({
|
||||
"type": "text",
|
||||
"text": CLAUDE_CODE_PREFIX,
|
||||
});
|
||||
|
||||
match body.get("system") {
|
||||
Some(Value::String(existing)) => {
|
||||
let existing_block = json!({
|
||||
"type": "text",
|
||||
"text": existing,
|
||||
});
|
||||
body["system"] = json!([prefix_block, existing_block]);
|
||||
}
|
||||
Some(Value::Array(_)) => {
|
||||
if let Some(arr) = body["system"].as_array_mut() {
|
||||
let already_injected = arr
|
||||
.iter()
|
||||
.any(|block| block["text"].as_str() == Some(CLAUDE_CODE_PREFIX));
|
||||
if !already_injected {
|
||||
arr.insert(0, prefix_block);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
body["system"] = json!([prefix_block]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn claude_chat_completions(
|
||||
builder: RequestBuilder,
|
||||
_model: &Model,
|
||||
|
||||
@@ -177,6 +177,10 @@ impl Model {
|
||||
self.data.max_output_tokens
|
||||
}
|
||||
|
||||
pub fn supports_function_calling(&self) -> bool {
|
||||
self.data.supports_function_calling
|
||||
}
|
||||
|
||||
pub fn no_stream(&self) -> bool {
|
||||
self.data.no_stream
|
||||
}
|
||||
|
||||
+10
-5
@@ -239,12 +239,17 @@ impl Input {
|
||||
patch_messages(&mut messages, model);
|
||||
model.guard_max_input_tokens(&messages)?;
|
||||
let (temperature, top_p) = (self.role().temperature(), self.role().top_p());
|
||||
let functions = self.config.read().select_functions(self.role());
|
||||
if let Some(vec) = &functions {
|
||||
for def in vec {
|
||||
debug!("Function definition: {:?}", def.name);
|
||||
let functions = if model.supports_function_calling() {
|
||||
let fns = self.config.read().select_functions(self.role());
|
||||
if let Some(vec) = &fns {
|
||||
for def in vec {
|
||||
debug!("Function definition: {:?}", def.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
fns
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(ChatCompletionsData {
|
||||
messages,
|
||||
temperature,
|
||||
|
||||
@@ -1842,6 +1842,12 @@ impl Config {
|
||||
bail!("Already in an agent, please run '.exit agent' first to exit the current agent.");
|
||||
}
|
||||
let agent = Agent::init(config, agent_name, abort_signal.clone()).await?;
|
||||
if !agent.model().supports_function_calling() {
|
||||
eprintln!(
|
||||
"Warning: The model '{}' does not support function calling. Agent tools (including todo, spawning, and user interaction) will not be available.",
|
||||
agent.model().id()
|
||||
);
|
||||
}
|
||||
let session = session_name.map(|v| v.to_string()).or_else(|| {
|
||||
if config.read().macro_flag {
|
||||
None
|
||||
|
||||
Reference in New Issue
Block a user