Fix provider context limits and AWS authentication routing
This commit is contained in:
@@ -43,6 +43,8 @@ pub(crate) struct RuntimeResponseTranslator {
|
||||
/// Usage for the most recent model call.
|
||||
usage: Usage,
|
||||
context_usage: Option<(u64, u64)>,
|
||||
// Set before starting each model call, including failures before TurnStarted.
|
||||
authentication_target: Option<(api::LlmProvider, String)>,
|
||||
}
|
||||
|
||||
/// Projects a multi-turn provider run into one existing Galaxy response stream.
|
||||
@@ -98,8 +100,16 @@ impl ProviderRunResponseProjector {
|
||||
ProviderRunProjection::ModelRetry { .. } => {
|
||||
Ok(self.translator.discard_failed_turn_output())
|
||||
}
|
||||
ProviderRunProjection::ModelTurnRequested { .. }
|
||||
| ProviderRunProjection::ModelTurnFinished { .. } => Ok(Vec::new()),
|
||||
ProviderRunProjection::ModelTurnRequested {
|
||||
runtime_id,
|
||||
model_id,
|
||||
..
|
||||
} => {
|
||||
self.translator.authentication_target =
|
||||
Some((authentication_provider_for_runtime(&runtime_id), model_id));
|
||||
Ok(Vec::new())
|
||||
}
|
||||
ProviderRunProjection::ModelTurnFinished { .. } => Ok(Vec::new()),
|
||||
ProviderRunProjection::ToolBatchReady { batch } => {
|
||||
if !self.plan_tasks_projected {
|
||||
if let Some(todos) = todos_from_plan_batch(&batch) {
|
||||
@@ -149,6 +159,19 @@ impl ProviderRunResponseProjector {
|
||||
}
|
||||
}
|
||||
|
||||
// Runtime IDs identify the transport, unlike model IDs: a Bedrock-backed model
|
||||
// behind LiteLLM still authenticates against the OpenAI-compatible endpoint.
|
||||
fn authentication_provider_for_runtime(runtime_id: &str) -> api::LlmProvider {
|
||||
match runtime_id.split_once(':').map(|(runtime, _)| runtime) {
|
||||
Some("rig-bedrock") => api::LlmProvider::AwsBedrock,
|
||||
Some("rig-openai-compatible" | "rig-chatgpt-subscription") => api::LlmProvider::Openai,
|
||||
Some("rig-anthropic") => api::LlmProvider::Anthropic,
|
||||
Some("rig-gemini" | "rig-vertex-ai") => api::LlmProvider::Google,
|
||||
// Unrecognized runtimes must never trigger provider-specific login commands.
|
||||
Some(_) | None => api::LlmProvider::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
// Only explicit multi-step checklists in a plan become tasks. Other document bullets
|
||||
// (risks, examples, requirements) are not executable tasks.
|
||||
fn todos_from_plan_batch(
|
||||
@@ -251,6 +274,7 @@ impl RuntimeResponseTranslator {
|
||||
has_visible_output: false,
|
||||
usage: Usage::default(),
|
||||
context_usage: None,
|
||||
authentication_target: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,8 +552,15 @@ impl RuntimeResponseTranslator {
|
||||
.is_some_and(|error| error.kind == galaxy_agent_core::AgentErrorKind::Authentication)
|
||||
{
|
||||
stream_finished::Reason::InvalidApiKey(stream_finished::InvalidApiKey {
|
||||
provider: warp_multi_agent_api::LlmProvider::AwsBedrock as i32,
|
||||
model_name: self.config.model_id.clone(),
|
||||
provider: self
|
||||
.authentication_target
|
||||
.as_ref()
|
||||
.map_or(api::LlmProvider::Unknown, |(provider, _)| *provider)
|
||||
as i32,
|
||||
model_name: self
|
||||
.authentication_target
|
||||
.as_ref()
|
||||
.map_or_else(|| self.config.model_id.clone(), |(_, model)| model.clone()),
|
||||
})
|
||||
} else {
|
||||
stream_finished::Reason::InternalError(stream_finished::InternalError {
|
||||
|
||||
@@ -640,3 +640,63 @@ fn plan_tasks_require_multiple_explicit_checklist_items_in_a_task_section() {
|
||||
assert_eq!(todos[1].title, "Test stale callbacks");
|
||||
assert_ne!(todos[0].id, todos[1].id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authentication_failures_identify_the_requested_transport_before_stream_start() {
|
||||
for (runtime, expected) in [
|
||||
(
|
||||
"rig-openai-compatible",
|
||||
warp_multi_agent_api::LlmProvider::Openai,
|
||||
),
|
||||
(
|
||||
"rig-chatgpt-subscription",
|
||||
warp_multi_agent_api::LlmProvider::Openai,
|
||||
),
|
||||
("rig-bedrock", warp_multi_agent_api::LlmProvider::AwsBedrock),
|
||||
(
|
||||
"rig-anthropic",
|
||||
warp_multi_agent_api::LlmProvider::Anthropic,
|
||||
),
|
||||
("rig-gemini", warp_multi_agent_api::LlmProvider::Google),
|
||||
("rig-vertex-ai", warp_multi_agent_api::LlmProvider::Google),
|
||||
("unrecognized", warp_multi_agent_api::LlmProvider::Unknown),
|
||||
] {
|
||||
let mut projector = ProviderRunResponseProjector::new(provider_translator().config);
|
||||
// A previous Bedrock turn must not leak its identity into the next request.
|
||||
for runtime_id in [
|
||||
"rig-bedrock:old-model".to_owned(),
|
||||
format!("{runtime}:new-model"),
|
||||
] {
|
||||
projector
|
||||
.project(ProviderRunProjection::ModelTurnRequested {
|
||||
work_id: galaxy_agent_core::ExternalWorkId {
|
||||
run_id: galaxy_agent_core::ProviderRunId::new("run"),
|
||||
epoch: galaxy_agent_core::RunEpoch::new(1),
|
||||
},
|
||||
profile: galaxy_agent_core::ProviderRequestProfile::new("cli"),
|
||||
runtime_id,
|
||||
model_id: "new-model".to_owned(),
|
||||
retry_attempt: 0,
|
||||
})
|
||||
.expect("requested turn projection");
|
||||
}
|
||||
let error = galaxy_agent_core::AgentError::new(
|
||||
galaxy_agent_core::AgentErrorKind::Authentication,
|
||||
"Unauthorized",
|
||||
);
|
||||
let events =
|
||||
projector
|
||||
.translator
|
||||
.provider_failure("Unauthorized", Some(&error), &Usage::default());
|
||||
let Some(response_event::Type::Finished(finished)) = &events.last().unwrap().r#type else {
|
||||
panic!("expected a finished event");
|
||||
};
|
||||
let Some(response_event::stream_finished::Reason::InvalidApiKey(details)) =
|
||||
&finished.reason
|
||||
else {
|
||||
panic!("expected authentication failure");
|
||||
};
|
||||
assert_eq!(details.provider, expected as i32, "{runtime}");
|
||||
assert_eq!(details.model_name, "new-model");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user