Compare commits
2 Commits
f41c85b703
...
d78820dcd4
| Author | SHA1 | Date | |
|---|---|---|---|
|
d78820dcd4
|
|||
|
d43c4232a2
|
@@ -11,6 +11,7 @@ use serde::Deserialize;
|
|||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
|
|
||||||
const API_BASE: &str = "https://api.anthropic.com/v1";
|
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)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct ClaudeConfig {
|
pub struct ClaudeConfig {
|
||||||
@@ -94,6 +95,7 @@ async fn prepare_chat_completions(
|
|||||||
for (key, value) in provider.extra_request_headers() {
|
for (key, value) in provider.extra_request_headers() {
|
||||||
request_data.header(key, value);
|
request_data.header(key, value);
|
||||||
}
|
}
|
||||||
|
inject_oauth_system_prompt(&mut request_data.body);
|
||||||
} else if let Ok(api_key) = self_.get_api_key() {
|
} else if let Ok(api_key) = self_.get_api_key() {
|
||||||
request_data.header("x-api-key", api_key);
|
request_data.header("x-api-key", api_key);
|
||||||
} else {
|
} else {
|
||||||
@@ -107,6 +109,43 @@ async fn prepare_chat_completions(
|
|||||||
Ok(request_data)
|
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(
|
pub async fn claude_chat_completions(
|
||||||
builder: RequestBuilder,
|
builder: RequestBuilder,
|
||||||
_model: &Model,
|
_model: &Model,
|
||||||
|
|||||||
@@ -177,6 +177,10 @@ impl Model {
|
|||||||
self.data.max_output_tokens
|
self.data.max_output_tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn supports_function_calling(&self) -> bool {
|
||||||
|
self.data.supports_function_calling
|
||||||
|
}
|
||||||
|
|
||||||
pub fn no_stream(&self) -> bool {
|
pub fn no_stream(&self) -> bool {
|
||||||
self.data.no_stream
|
self.data.no_stream
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-5
@@ -239,12 +239,17 @@ impl Input {
|
|||||||
patch_messages(&mut messages, model);
|
patch_messages(&mut messages, model);
|
||||||
model.guard_max_input_tokens(&messages)?;
|
model.guard_max_input_tokens(&messages)?;
|
||||||
let (temperature, top_p) = (self.role().temperature(), self.role().top_p());
|
let (temperature, top_p) = (self.role().temperature(), self.role().top_p());
|
||||||
let functions = self.config.read().select_functions(self.role());
|
let functions = if model.supports_function_calling() {
|
||||||
if let Some(vec) = &functions {
|
let fns = self.config.read().select_functions(self.role());
|
||||||
for def in vec {
|
if let Some(vec) = &fns {
|
||||||
debug!("Function definition: {:?}", def.name);
|
for def in vec {
|
||||||
|
debug!("Function definition: {:?}", def.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
fns
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
Ok(ChatCompletionsData {
|
Ok(ChatCompletionsData {
|
||||||
messages,
|
messages,
|
||||||
temperature,
|
temperature,
|
||||||
|
|||||||
@@ -1842,6 +1842,12 @@ impl Config {
|
|||||||
bail!("Already in an agent, please run '.exit agent' first to exit the current agent.");
|
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?;
|
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(|| {
|
let session = session_name.map(|v| v.to_string()).or_else(|| {
|
||||||
if config.read().macro_flag {
|
if config.read().macro_flag {
|
||||||
None
|
None
|
||||||
|
|||||||
Reference in New Issue
Block a user