fix: handle InvokeSkill in request_translator to prevent empty message error
When a skill was invoked on a clean slate (no conversation history),
the request sent to the AI provider contained only a system prompt
with zero user messages, causing Bedrock/LiteLLM to reject it with:
'Invalid Message bedrock requires at least one non-system message'
Root cause: extract_new_input_messages() had no handler for
Type::InvokeSkill, so it fell through to the _ => {} wildcard and
produced zero ConversationMessage results.
Fix adds InvokeSkill handling to three functions:
- extract_new_input_messages(): generates a User message with the
skill name and content wrapped in <skill-instructions> tags
- extract_input_messages(): persists the InvokeSkill as a proper
api::Message in task history for conversation continuity
- extract_user_query_text(): returns '/<skill-name>' for the
UserQuery proto event used in conversation title generation
This commit is contained in:
@@ -224,6 +224,42 @@ pub fn extract_new_input_messages(request: &api::Request) -> Vec<ConversationMes
|
||||
content: MessageContent::Text(prompt),
|
||||
});
|
||||
}
|
||||
api::request::input::Type::InvokeSkill(invoke_skill) => {
|
||||
if let Some(skill) = &invoke_skill.skill {
|
||||
let skill_name = skill
|
||||
.descriptor
|
||||
.as_ref()
|
||||
.map(|d| d.name.as_str())
|
||||
.unwrap_or("unknown");
|
||||
let skill_content = skill
|
||||
.content
|
||||
.as_ref()
|
||||
.map(|c| c.content.as_str())
|
||||
.unwrap_or("");
|
||||
let user_query_text = invoke_skill
|
||||
.user_query
|
||||
.as_ref()
|
||||
.map(|q| q.query.as_str())
|
||||
.unwrap_or("");
|
||||
|
||||
let query = if user_query_text.is_empty() {
|
||||
format!(
|
||||
"Execute the following skill: {skill_name}\n\n\
|
||||
<skill-instructions>\n{skill_content}\n</skill-instructions>"
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"Execute the following skill: {skill_name}\n\n\
|
||||
<skill-instructions>\n{skill_content}\n</skill-instructions>\n\n\
|
||||
Additional context from user: {user_query_text}"
|
||||
)
|
||||
};
|
||||
results.push(ConversationMessage {
|
||||
role: MessageRole::User,
|
||||
content: MessageContent::Text(query),
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -285,6 +321,19 @@ pub fn extract_user_query_text(request: &api::Request) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
api::request::input::Type::InvokeSkill(invoke_skill) => {
|
||||
let skill_name = invoke_skill
|
||||
.skill
|
||||
.as_ref()
|
||||
.and_then(|s| s.descriptor.as_ref())
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
if skill_name.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(format!("/{skill_name}"))
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -508,6 +557,65 @@ fn extract_input_messages(request: &api::Request) -> Vec<api::Message> {
|
||||
})),
|
||||
});
|
||||
}
|
||||
api::request::input::Type::InvokeSkill(invoke_skill) => {
|
||||
if let Some(skill) = &invoke_skill.skill {
|
||||
let skill_name = skill
|
||||
.descriptor
|
||||
.as_ref()
|
||||
.map(|d| d.name.as_str())
|
||||
.unwrap_or("unknown");
|
||||
let skill_content = skill
|
||||
.content
|
||||
.as_ref()
|
||||
.map(|c| c.content.as_str())
|
||||
.unwrap_or("");
|
||||
let user_query_text = invoke_skill
|
||||
.user_query
|
||||
.as_ref()
|
||||
.map(|q| q.query.as_str())
|
||||
.unwrap_or("");
|
||||
|
||||
let query = if user_query_text.is_empty() {
|
||||
format!(
|
||||
"Execute the following skill: {skill_name}\n\n\
|
||||
<skill-instructions>\n{skill_content}\n</skill-instructions>"
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"Execute the following skill: {skill_name}\n\n\
|
||||
<skill-instructions>\n{skill_content}\n</skill-instructions>\n\n\
|
||||
Additional context from user: {user_query_text}"
|
||||
)
|
||||
};
|
||||
let message_user_query =
|
||||
invoke_skill.user_query.as_ref().map(|input_query| {
|
||||
api::message::UserQuery {
|
||||
query: input_query.query.clone(),
|
||||
context: None,
|
||||
referenced_attachments: input_query
|
||||
.referenced_attachments
|
||||
.clone(),
|
||||
mode: input_query.mode,
|
||||
intended_agent: input_query.intended_agent,
|
||||
}
|
||||
});
|
||||
results.push(api::Message {
|
||||
id: uuid::Uuid::new_v4().to_string(),
|
||||
task_id: task_id.clone(),
|
||||
request_id: String::new(),
|
||||
timestamp: None,
|
||||
server_message_data: String::new(),
|
||||
citations: vec![],
|
||||
fetched_memories: vec![],
|
||||
message: Some(api::message::Message::InvokeSkill(
|
||||
api::message::InvokeSkill {
|
||||
skill: invoke_skill.skill.clone(),
|
||||
user_query: message_user_query,
|
||||
},
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user