fix: model narration included in history and between tool calls to prevent repetition
This commit is contained in:
@@ -397,6 +397,11 @@ fn build_chat_completions_body(data: ChatCompletionsData, model: &Model) -> Resu
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
for tool_result in tool_results {
|
for tool_result in tool_results {
|
||||||
|
if let Some(round_text) = &tool_result.text {
|
||||||
|
assistant_parts.push(json!({
|
||||||
|
"text": round_text,
|
||||||
|
}))
|
||||||
|
}
|
||||||
assistant_parts.push(json!({
|
assistant_parts.push(json!({
|
||||||
"toolUse": {
|
"toolUse": {
|
||||||
"toolUseId": tool_result.call.id,
|
"toolUseId": tool_result.call.id,
|
||||||
|
|||||||
@@ -320,6 +320,12 @@ pub fn claude_build_chat_completions_body(
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
for tool_result in tool_results {
|
for tool_result in tool_results {
|
||||||
|
if let Some(round_text) = &tool_result.text {
|
||||||
|
assistant_parts.push(json!({
|
||||||
|
"type": "text",
|
||||||
|
"text": round_text,
|
||||||
|
}))
|
||||||
|
}
|
||||||
assistant_parts.push(json!({
|
assistant_parts.push(json!({
|
||||||
"type": "tool_use",
|
"type": "tool_use",
|
||||||
"id": tool_result.call.id,
|
"id": tool_result.call.id,
|
||||||
|
|||||||
@@ -118,6 +118,9 @@ impl MessageContent {
|
|||||||
lines.push(text.clone())
|
lines.push(text.clone())
|
||||||
}
|
}
|
||||||
for tool_result in tool_results {
|
for tool_result in tool_results {
|
||||||
|
if let Some(round_text) = &tool_result.text {
|
||||||
|
lines.push(round_text.clone())
|
||||||
|
}
|
||||||
let mut parts = vec!["Call".to_string()];
|
let mut parts = vec!["Call".to_string()];
|
||||||
if let Some((agent_name, functions)) = agent_info
|
if let Some((agent_name, functions)) = agent_info
|
||||||
&& functions.contains(&tool_result.call.name)
|
&& functions.contains(&tool_result.call.name)
|
||||||
@@ -201,9 +204,13 @@ impl MessageContentToolCalls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn merge(&mut self, tool_results: Vec<ToolResult>, _text: String) {
|
pub fn merge(&mut self, mut tool_results: Vec<ToolResult>, text: String) {
|
||||||
|
if !text.is_empty()
|
||||||
|
&& let Some(first) = tool_results.first_mut()
|
||||||
|
{
|
||||||
|
first.text = Some(text);
|
||||||
|
}
|
||||||
self.tool_results.extend(tool_results);
|
self.tool_results.extend(tool_results);
|
||||||
self.text.clear();
|
|
||||||
self.sequence = true;
|
self.sequence = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-33
@@ -370,7 +370,7 @@ pub fn openai_build_chat_completions_body(data: ChatCompletionsData, model: &Mod
|
|||||||
match content {
|
match content {
|
||||||
MessageContent::ToolCalls(MessageContentToolCalls {
|
MessageContent::ToolCalls(MessageContentToolCalls {
|
||||||
tool_results,
|
tool_results,
|
||||||
text: _,
|
text,
|
||||||
sequence,
|
sequence,
|
||||||
}) => {
|
}) => {
|
||||||
if !sequence {
|
if !sequence {
|
||||||
@@ -387,9 +387,12 @@ pub fn openai_build_chat_completions_body(data: ChatCompletionsData, model: &Mod
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let mut messages = vec![
|
let mut assistant_message =
|
||||||
json!({ "role": MessageRole::Assistant, "tool_calls": tool_calls }),
|
json!({ "role": MessageRole::Assistant, "tool_calls": tool_calls });
|
||||||
];
|
if !text.is_empty() {
|
||||||
|
assistant_message["content"] = strip_think_tag(&text).into();
|
||||||
|
}
|
||||||
|
let mut messages = vec![assistant_message];
|
||||||
for tool_result in tool_results {
|
for tool_result in tool_results {
|
||||||
messages.push(json!({
|
messages.push(json!({
|
||||||
"role": "tool",
|
"role": "tool",
|
||||||
@@ -399,21 +402,30 @@ pub fn openai_build_chat_completions_body(data: ChatCompletionsData, model: &Mod
|
|||||||
}
|
}
|
||||||
messages
|
messages
|
||||||
} else {
|
} else {
|
||||||
tool_results.into_iter().flat_map(|tool_result| {
|
tool_results.into_iter().enumerate().flat_map(|(index, tool_result)| {
|
||||||
|
let round_text = if index == 0 && !text.is_empty() {
|
||||||
|
Some(text.clone())
|
||||||
|
} else {
|
||||||
|
tool_result.text.clone()
|
||||||
|
};
|
||||||
|
let mut assistant_message = json!({
|
||||||
|
"role": MessageRole::Assistant,
|
||||||
|
"tool_calls": [
|
||||||
|
{
|
||||||
|
"id": tool_result.call.id,
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": tool_result.call.name,
|
||||||
|
"arguments": tool_result.call.arguments.to_string(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
if let Some(round_text) = round_text {
|
||||||
|
assistant_message["content"] = strip_think_tag(&round_text).into();
|
||||||
|
}
|
||||||
vec![
|
vec![
|
||||||
json!({
|
assistant_message,
|
||||||
"role": MessageRole::Assistant,
|
|
||||||
"tool_calls": [
|
|
||||||
{
|
|
||||||
"id": tool_result.call.id,
|
|
||||||
"type": "function",
|
|
||||||
"function": {
|
|
||||||
"name": tool_result.call.name,
|
|
||||||
"arguments": tool_result.call.arguments.to_string(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
json!({
|
json!({
|
||||||
"role": "tool",
|
"role": "tool",
|
||||||
"content": tool_result.output.to_string(),
|
"content": tool_result.output.to_string(),
|
||||||
@@ -552,24 +564,36 @@ pub fn openai_build_responses_body(data: ChatCompletionsData, model: &Model) ->
|
|||||||
match content {
|
match content {
|
||||||
MessageContent::ToolCalls(MessageContentToolCalls {
|
MessageContent::ToolCalls(MessageContentToolCalls {
|
||||||
tool_results,
|
tool_results,
|
||||||
text: _,
|
text,
|
||||||
sequence: _,
|
sequence: _,
|
||||||
}) => tool_results
|
}) => tool_results
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|tool_result| {
|
.enumerate()
|
||||||
vec![
|
.flat_map(|(index, tool_result)| {
|
||||||
json!({
|
let round_text = if index == 0 && !text.is_empty() {
|
||||||
"type": "function_call",
|
Some(text.clone())
|
||||||
"call_id": tool_result.call.id,
|
} else {
|
||||||
"name": tool_result.call.name,
|
tool_result.text.clone()
|
||||||
"arguments": tool_result.call.arguments.to_string(),
|
};
|
||||||
}),
|
let mut items = vec![];
|
||||||
json!({
|
if let Some(round_text) = round_text {
|
||||||
"type": "function_call_output",
|
items.push(json!({
|
||||||
"call_id": tool_result.call.id,
|
"role": MessageRole::Assistant,
|
||||||
"output": tool_result.output.to_string(),
|
"content": strip_think_tag(&round_text),
|
||||||
}),
|
}));
|
||||||
]
|
}
|
||||||
|
items.push(json!({
|
||||||
|
"type": "function_call",
|
||||||
|
"call_id": tool_result.call.id,
|
||||||
|
"name": tool_result.call.name,
|
||||||
|
"arguments": tool_result.call.arguments.to_string(),
|
||||||
|
}));
|
||||||
|
items.push(json!({
|
||||||
|
"type": "function_call_output",
|
||||||
|
"call_id": tool_result.call.id,
|
||||||
|
"output": tool_result.output.to_string(),
|
||||||
|
}));
|
||||||
|
items
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
MessageContent::Text(text) if role.is_assistant() && i != messages_len - 1 => {
|
MessageContent::Text(text) if role.is_assistant() && i != messages_len - 1 => {
|
||||||
|
|||||||
+11
-4
@@ -372,8 +372,15 @@ pub fn gemini_build_chat_completions_body(
|
|||||||
.collect();
|
.collect();
|
||||||
vec![json!({ "role": role, "parts": parts })]
|
vec![json!({ "role": role, "parts": parts })]
|
||||||
},
|
},
|
||||||
MessageContent::ToolCalls(MessageContentToolCalls { tool_results, .. }) => {
|
MessageContent::ToolCalls(MessageContentToolCalls { tool_results, text, .. }) => {
|
||||||
let model_parts: Vec<Value> = tool_results.iter().map(|tool_result| {
|
let mut model_parts: Vec<Value> = vec![];
|
||||||
|
if !text.is_empty() {
|
||||||
|
model_parts.push(json!({ "text": text }));
|
||||||
|
}
|
||||||
|
for tool_result in tool_results.iter() {
|
||||||
|
if let Some(round_text) = &tool_result.text {
|
||||||
|
model_parts.push(json!({ "text": round_text }));
|
||||||
|
}
|
||||||
let mut part = json!({
|
let mut part = json!({
|
||||||
"functionCall": {
|
"functionCall": {
|
||||||
"name": tool_result.call.name,
|
"name": tool_result.call.name,
|
||||||
@@ -383,8 +390,8 @@ pub fn gemini_build_chat_completions_body(
|
|||||||
if let Some(sig) = &tool_result.call.thought_signature {
|
if let Some(sig) = &tool_result.call.thought_signature {
|
||||||
part["thoughtSignature"] = json!(sig);
|
part["thoughtSignature"] = json!(sig);
|
||||||
}
|
}
|
||||||
part
|
model_parts.push(part);
|
||||||
}).collect();
|
}
|
||||||
let function_parts: Vec<Value> = tool_results.into_iter().map(|tool_result| {
|
let function_parts: Vec<Value> = tool_results.into_iter().map(|tool_result| {
|
||||||
json!({
|
json!({
|
||||||
"functionResponse": {
|
"functionResponse": {
|
||||||
|
|||||||
+7
-1
@@ -203,11 +203,17 @@ fn normalize_tool_result(result: Value) -> Value {
|
|||||||
pub struct ToolResult {
|
pub struct ToolResult {
|
||||||
pub call: ToolCall,
|
pub call: ToolCall,
|
||||||
pub output: Value,
|
pub output: Value,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub text: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToolResult {
|
impl ToolResult {
|
||||||
pub fn new(call: ToolCall, output: Value) -> Self {
|
pub fn new(call: ToolCall, output: Value) -> Self {
|
||||||
Self { call, output }
|
Self {
|
||||||
|
call,
|
||||||
|
output,
|
||||||
|
text: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user