feat(agent): add architect, debugger, implementer, tester, security sub-agent roles
Extend delegation system with 5 new specialized roles alongside researcher/analyst/reviewer. Each role has curated tool access. Refactor profile lookup to use profile_for_role_name and update compact/summarizer and tool context accordingly.
This commit is contained in:
parent
b413edccaf
commit
8d144ac139
@ -15,6 +15,7 @@ fn shared_tools() -> Vec<String> {
|
||||
"git_file_content".into(),
|
||||
"git_blob_get".into(),
|
||||
"git_blob_content".into(),
|
||||
"git_status".into(),
|
||||
]
|
||||
}
|
||||
|
||||
@ -25,8 +26,10 @@ fn researcher_tools() -> Vec<String> {
|
||||
"git_search_commits".into(),
|
||||
"repo_search".into(),
|
||||
"repo_doc_search".into(),
|
||||
"project_bing_search".into(),
|
||||
"project_arxiv_search".into(),
|
||||
"project_curl".into(),
|
||||
"curl_exec".into(),
|
||||
// Index / overview
|
||||
"repo_overview".into(),
|
||||
"repo_readme".into(),
|
||||
@ -45,6 +48,10 @@ fn researcher_tools() -> Vec<String> {
|
||||
"git_graph".into(),
|
||||
"git_commit_info".into(),
|
||||
"repo_commit_log".into(),
|
||||
"git_ref_list".into(),
|
||||
"git_ref_info".into(),
|
||||
"git_lfs_summary".into(),
|
||||
"git_lfs_scan_tree".into(),
|
||||
]
|
||||
}
|
||||
|
||||
@ -65,6 +72,9 @@ fn analyst_tools() -> Vec<String> {
|
||||
"git_branch_list".into(),
|
||||
"git_branch_info".into(),
|
||||
"git_branch_diff".into(),
|
||||
"git_merge_analysis".into(),
|
||||
"git_ref_list".into(),
|
||||
"git_ref_info".into(),
|
||||
// Project data
|
||||
"project_list_issues".into(),
|
||||
"project_list_repos".into(),
|
||||
@ -81,6 +91,7 @@ fn reviewer_tools() -> Vec<String> {
|
||||
// Merge status
|
||||
"git_branches_merged".into(),
|
||||
"git_branch_info".into(),
|
||||
"git_merge_analysis".into(),
|
||||
// Tracking
|
||||
"project_list_issues".into(),
|
||||
"project_update_issue".into(),
|
||||
@ -90,6 +101,108 @@ fn reviewer_tools() -> Vec<String> {
|
||||
]
|
||||
}
|
||||
|
||||
/// Architect-specific tools (system design & dependency mapping).
|
||||
fn architect_tools() -> Vec<String> {
|
||||
vec![
|
||||
// Repository structure
|
||||
"repo_overview".into(),
|
||||
"repo_readme".into(),
|
||||
"repo_file_tree".into(),
|
||||
"repo_languages".into(),
|
||||
"repo_dependencies".into(),
|
||||
"repo_test_discovery".into(),
|
||||
// Change and branch context
|
||||
"repo_diff_summary".into(),
|
||||
"git_branch_list".into(),
|
||||
"git_branch_info".into(),
|
||||
"git_branch_diff".into(),
|
||||
"git_merge_analysis".into(),
|
||||
"git_ref_list".into(),
|
||||
// Project context
|
||||
"project_list_repos".into(),
|
||||
"project_list_issues".into(),
|
||||
]
|
||||
}
|
||||
|
||||
/// Debugger-specific tools (root-cause analysis & history tracing).
|
||||
fn debugger_tools() -> Vec<String> {
|
||||
vec![
|
||||
// Failure and change inspection
|
||||
"git_show".into(),
|
||||
"git_diff".into(),
|
||||
"git_diff_stats".into(),
|
||||
"git_blame".into(),
|
||||
"git_log".into(),
|
||||
"git_commit_info".into(),
|
||||
"git_ref_info".into(),
|
||||
// Structural clues
|
||||
"repo_file_tree".into(),
|
||||
"repo_dependencies".into(),
|
||||
"repo_test_discovery".into(),
|
||||
"repo_doc_search".into(),
|
||||
// Issue context
|
||||
"project_list_issues".into(),
|
||||
]
|
||||
}
|
||||
|
||||
/// Implementer-specific tools (implementation planning & code navigation).
|
||||
fn implementer_tools() -> Vec<String> {
|
||||
vec![
|
||||
// Code and documentation context
|
||||
"repo_overview".into(),
|
||||
"repo_readme".into(),
|
||||
"repo_file_tree".into(),
|
||||
"repo_doc_index".into(),
|
||||
"repo_doc_read".into(),
|
||||
"repo_dependencies".into(),
|
||||
"repo_test_discovery".into(),
|
||||
// Current change context
|
||||
"git_diff".into(),
|
||||
"git_diff_stats".into(),
|
||||
"git_show".into(),
|
||||
"git_branch_info".into(),
|
||||
// Project context
|
||||
"project_list_issues".into(),
|
||||
]
|
||||
}
|
||||
|
||||
/// Tester-specific tools (coverage, regression, and validation planning).
|
||||
fn tester_tools() -> Vec<String> {
|
||||
vec![
|
||||
// Test discovery and changed surface
|
||||
"git_grep".into(),
|
||||
"git_diff".into(),
|
||||
"git_diff_stats".into(),
|
||||
"git_show".into(),
|
||||
"repo_file_tree".into(),
|
||||
"repo_dependencies".into(),
|
||||
"repo_test_discovery".into(),
|
||||
// Project context
|
||||
"project_list_issues".into(),
|
||||
]
|
||||
}
|
||||
|
||||
/// Security-specific tools (threat modeling & sensitive-code review).
|
||||
fn security_tools() -> Vec<String> {
|
||||
vec![
|
||||
// Sensitive pattern discovery
|
||||
"git_grep".into(),
|
||||
"git_diff".into(),
|
||||
"git_diff_stats".into(),
|
||||
"git_blame".into(),
|
||||
"git_log".into(),
|
||||
// Dependency and surface review
|
||||
"repo_file_tree".into(),
|
||||
"repo_dependencies".into(),
|
||||
"repo_doc_search".into(),
|
||||
"git_lfs_summary".into(),
|
||||
"git_lfs_pointer_info".into(),
|
||||
"git_lfs_object_info".into(),
|
||||
// Issue context
|
||||
"project_list_issues".into(),
|
||||
]
|
||||
}
|
||||
|
||||
/// Supervisor-specific tools (delegation & synthesis).
|
||||
fn supervisor_tools() -> Vec<String> {
|
||||
vec![
|
||||
@ -105,12 +218,31 @@ pub fn tools_for_role(role: &AgentRole) -> Vec<String> {
|
||||
AgentRole::Researcher => tools.extend(researcher_tools()),
|
||||
AgentRole::Analyst => tools.extend(analyst_tools()),
|
||||
AgentRole::Reviewer => tools.extend(reviewer_tools()),
|
||||
AgentRole::Architect => tools.extend(architect_tools()),
|
||||
AgentRole::Debugger => tools.extend(debugger_tools()),
|
||||
AgentRole::Implementer => tools.extend(implementer_tools()),
|
||||
AgentRole::Tester => tools.extend(tester_tools()),
|
||||
AgentRole::Security => tools.extend(security_tools()),
|
||||
AgentRole::Supervisor => tools.extend(supervisor_tools()),
|
||||
AgentRole::Default => {} // Default role gets only shared tools
|
||||
}
|
||||
tools
|
||||
}
|
||||
|
||||
pub fn profile_for_role_name(role: &str) -> AgentExecutionProfile {
|
||||
match role {
|
||||
"researcher" => researcher_profile(),
|
||||
"analyst" => analyst_profile(),
|
||||
"reviewer" => reviewer_profile(),
|
||||
"architect" => architect_profile(),
|
||||
"debugger" => debugger_profile(),
|
||||
"implementer" => implementer_profile(),
|
||||
"tester" => tester_profile(),
|
||||
"security" => security_profile(),
|
||||
_ => researcher_profile(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn supervisor_profile() -> AgentExecutionProfile {
|
||||
AgentExecutionProfile {
|
||||
role: AgentRole::Supervisor,
|
||||
@ -123,6 +255,11 @@ pub fn supervisor_profile() -> AgentExecutionProfile {
|
||||
- **researcher**: Gathers concrete facts, evidence, and data from tools and context. Best for finding information, searching code, and discovering evidence.\n\
|
||||
- **analyst**: Builds coherent explanations, highlights causal links, edge cases, and tradeoffs. Best for explaining findings and reasoning about implications.\n\
|
||||
- **reviewer**: Stress-tests proposals, identifies contradictions, missing assumptions, regressions, and risks. Best for quality checks and risk assessment.\n\
|
||||
- **architect**: Maps systems, boundaries, dependencies, and long-term design tradeoffs. Best for architecture decisions and refactor strategy.\n\
|
||||
- **debugger**: Finds likely root causes, reproduction gaps, and suspect changes. Best for failures, regressions, and confusing behavior.\n\
|
||||
- **implementer**: Turns requirements into concrete implementation steps, affected files, and integration concerns. Best for execution planning.\n\
|
||||
- **tester**: Designs validation strategy, regression coverage, and edge-case test plans. Best for test planning and release confidence.\n\
|
||||
- **security**: Reviews auth, data exposure, injection, dependency, and abuse risks. Best for threat modeling and sensitive changes.\n\
|
||||
- Provide a clear, focused task description for each sub-agent.\n\
|
||||
- You may call multiple sub-agents in sequence (call one, review its output, then decide to call another).\n\
|
||||
- You may also call the same role twice with different tasks if needed.\n\
|
||||
@ -130,7 +267,10 @@ pub fn supervisor_profile() -> AgentExecutionProfile {
|
||||
## Decision Guide\n\
|
||||
- Simple factual questions: call researcher only.\n\
|
||||
- Questions requiring explanation: call researcher then analyst.\n\
|
||||
- Design/architecture reviews: call researcher, analyst, then reviewer.\n\
|
||||
- Design/architecture reviews: call researcher, architect, then reviewer.\n\
|
||||
- Bug or regression diagnosis: call debugger, then tester if validation is needed.\n\
|
||||
- Implementation requests: call implementer, then reviewer for risk checks.\n\
|
||||
- Security-sensitive changes: call security, then reviewer.\n\
|
||||
- If a sub-agent's output is insufficient, call another sub-agent for clarification.\n\
|
||||
\n\
|
||||
## Output Rules\n\
|
||||
@ -201,8 +341,93 @@ pub fn reviewer_profile() -> AgentExecutionProfile {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn architect_profile() -> AgentExecutionProfile {
|
||||
AgentExecutionProfile {
|
||||
role: AgentRole::Architect,
|
||||
system_prompt: Some(
|
||||
"You are the architect agent. Map system boundaries, dependencies, data flow, and design tradeoffs. Prefer practical architecture guidance tied to repository evidence. Call out migration risks and long-term maintainability concerns.".to_string(),
|
||||
),
|
||||
temperature: Some(0.2),
|
||||
max_tokens: Some(2000),
|
||||
top_p: Some(1.0),
|
||||
frequency_penalty: Some(0.0),
|
||||
presence_penalty: Some(0.0),
|
||||
max_tool_depth: Some(5),
|
||||
allowed_tools: Some(tools_for_role(&AgentRole::Architect)),
|
||||
disable_orchestration: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn debugger_profile() -> AgentExecutionProfile {
|
||||
AgentExecutionProfile {
|
||||
role: AgentRole::Debugger,
|
||||
system_prompt: Some(
|
||||
"You are the debugger agent. Identify likely root causes, suspect files or commits, missing reproduction details, and the shortest validation path. Separate evidence from hypotheses and rank hypotheses by plausibility.".to_string(),
|
||||
),
|
||||
temperature: Some(0.1),
|
||||
max_tokens: Some(1800),
|
||||
top_p: Some(1.0),
|
||||
frequency_penalty: Some(0.0),
|
||||
presence_penalty: Some(0.0),
|
||||
max_tool_depth: Some(6),
|
||||
allowed_tools: Some(tools_for_role(&AgentRole::Debugger)),
|
||||
disable_orchestration: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn implementer_profile() -> AgentExecutionProfile {
|
||||
AgentExecutionProfile {
|
||||
role: AgentRole::Implementer,
|
||||
system_prompt: Some(
|
||||
"You are the implementer agent. Convert requirements into a concrete execution plan: files to touch, sequencing, integration points, and risks. Keep recommendations actionable and avoid broad rewrites unless justified.".to_string(),
|
||||
),
|
||||
temperature: Some(0.15),
|
||||
max_tokens: Some(1800),
|
||||
top_p: Some(1.0),
|
||||
frequency_penalty: Some(0.0),
|
||||
presence_penalty: Some(0.0),
|
||||
max_tool_depth: Some(5),
|
||||
allowed_tools: Some(tools_for_role(&AgentRole::Implementer)),
|
||||
disable_orchestration: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tester_profile() -> AgentExecutionProfile {
|
||||
AgentExecutionProfile {
|
||||
role: AgentRole::Tester,
|
||||
system_prompt: Some(
|
||||
"You are the tester agent. Design high-signal validation: unit, integration, regression, and edge-case coverage. Identify what must be tested, what can be skipped, and the fastest commands or checks to build confidence.".to_string(),
|
||||
),
|
||||
temperature: Some(0.1),
|
||||
max_tokens: Some(1600),
|
||||
top_p: Some(1.0),
|
||||
frequency_penalty: Some(0.0),
|
||||
presence_penalty: Some(0.0),
|
||||
max_tool_depth: Some(4),
|
||||
allowed_tools: Some(tools_for_role(&AgentRole::Tester)),
|
||||
disable_orchestration: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn security_profile() -> AgentExecutionProfile {
|
||||
AgentExecutionProfile {
|
||||
role: AgentRole::Security,
|
||||
system_prompt: Some(
|
||||
"You are the security agent. Review authentication, authorization, data exposure, injection, dependency, secret-handling, and abuse-case risks. Prioritize exploitable issues and concrete mitigations over generic advice.".to_string(),
|
||||
),
|
||||
temperature: Some(0.1),
|
||||
max_tokens: Some(1800),
|
||||
top_p: Some(1.0),
|
||||
frequency_penalty: Some(0.0),
|
||||
presence_penalty: Some(0.0),
|
||||
max_tool_depth: Some(5),
|
||||
allowed_tools: Some(tools_for_role(&AgentRole::Security)),
|
||||
disable_orchestration: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether to enable multi-agent delegation for this request.
|
||||
/// Simplified from keyword-based gating: delegation is enabled when tools are available.
|
||||
pub fn should_enable_delegation(_input: &str, tools_available: bool) -> bool {
|
||||
tools_available
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::agent_profile::{analyst_profile, researcher_profile, reviewer_profile};
|
||||
use super::agent_profile::profile_for_role_name;
|
||||
use crate::client::AiClientConfig;
|
||||
use crate::client::types::{ChatRequestMessage, ToolCall};
|
||||
use crate::client::{StreamChunk, StreamChunkType, StreamedToolCall, call_stream};
|
||||
@ -629,13 +629,13 @@ pub async fn execute_chat_stream(
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "call_sub_agent",
|
||||
"description": "Delegate a task to a specialist sub-agent and receive its output.\nAvailable roles:\n- researcher: Gathers facts, evidence, and data. Best for finding information and searching code.\n- analyst: Builds explanations, highlights causal links and tradeoffs. Best for reasoning about implications.\n- reviewer: Stress-tests proposals, identifies risks and contradictions. Best for quality checks.\nProvide a clear, focused task description so the sub-agent knows exactly what to investigate.",
|
||||
"description": "Delegate a task to a specialist sub-agent and receive its output.\nAvailable roles:\n- researcher: Gathers facts, evidence, and data. Best for finding information and searching code.\n- analyst: Builds explanations, highlights causal links and tradeoffs. Best for reasoning about implications.\n- reviewer: Stress-tests proposals, identifies risks and contradictions. Best for quality checks.\n- architect: Maps systems, dependencies, boundaries, and design tradeoffs. Best for architecture decisions.\n- debugger: Finds root causes, suspect changes, and validation paths. Best for bugs and regressions.\n- implementer: Converts requirements into concrete implementation steps. Best for execution planning.\n- tester: Designs validation and regression coverage. Best for test strategy.\n- security: Reviews auth, data exposure, injection, dependency, and abuse risks. Best for sensitive changes.\nProvide a clear, focused task description so the sub-agent knows exactly what to investigate.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"role": {
|
||||
"type": "string",
|
||||
"description": "The sub-agent role to delegate to: researcher, analyst, or reviewer."
|
||||
"description": "The sub-agent role to delegate to: researcher, analyst, reviewer, architect, debugger, implementer, tester, or security."
|
||||
},
|
||||
"task": {
|
||||
"type": "string",
|
||||
@ -805,11 +805,7 @@ pub async fn execute_chat_stream(
|
||||
.unwrap_or("researcher");
|
||||
let task = args.get("task").and_then(|v| v.as_str()).unwrap_or("");
|
||||
|
||||
let profile = match role {
|
||||
"analyst" => analyst_profile(),
|
||||
"reviewer" => reviewer_profile(),
|
||||
_ => researcher_profile(),
|
||||
};
|
||||
let profile = profile_for_role_name(role);
|
||||
|
||||
// Generate children_id BEFORE starting sub-agent execution
|
||||
let sub_agent_id = format!("sub-agent-{}", Uuid::now_v7());
|
||||
|
||||
@ -87,6 +87,11 @@ pub enum AgentRole {
|
||||
Researcher,
|
||||
Analyst,
|
||||
Reviewer,
|
||||
Architect,
|
||||
Debugger,
|
||||
Implementer,
|
||||
Tester,
|
||||
Security,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::agent_profile::{
|
||||
analyst_profile, researcher_profile, reviewer_profile, should_enable_delegation,
|
||||
supervisor_profile,
|
||||
};
|
||||
use super::agent_profile::{profile_for_role_name, should_enable_delegation, supervisor_profile};
|
||||
use super::message_builder::MessageBuilder;
|
||||
use super::nonstreaming_execution::execute_process;
|
||||
use super::service::{ProcessResult, StreamResult};
|
||||
@ -67,9 +64,8 @@ pub async fn execute_orchestrated_process(
|
||||
supervisor_request.frequency_penalty = profile
|
||||
.frequency_penalty
|
||||
.unwrap_or(request.frequency_penalty);
|
||||
supervisor_request.presence_penalty = profile
|
||||
.presence_penalty
|
||||
.unwrap_or(request.presence_penalty);
|
||||
supervisor_request.presence_penalty =
|
||||
profile.presence_penalty.unwrap_or(request.presence_penalty);
|
||||
|
||||
execute_process(
|
||||
supervisor_request,
|
||||
@ -138,9 +134,8 @@ pub async fn execute_orchestrated_stream(
|
||||
supervisor_request.frequency_penalty = profile
|
||||
.frequency_penalty
|
||||
.unwrap_or(request.frequency_penalty);
|
||||
supervisor_request.presence_penalty = profile
|
||||
.presence_penalty
|
||||
.unwrap_or(request.presence_penalty);
|
||||
supervisor_request.presence_penalty =
|
||||
profile.presence_penalty.unwrap_or(request.presence_penalty);
|
||||
|
||||
super::streaming_execution::execute_process_stream(
|
||||
supervisor_request,
|
||||
@ -175,6 +170,11 @@ fn register_call_sub_agent_tool(
|
||||
- researcher: Gathers facts, evidence, and data. Best for finding information and searching code.\n\
|
||||
- analyst: Builds explanations, highlights causal links and tradeoffs. Best for reasoning about implications.\n\
|
||||
- reviewer: Stress-tests proposals, identifies risks and contradictions. Best for quality checks.\n\
|
||||
- architect: Maps systems, dependencies, boundaries, and design tradeoffs. Best for architecture decisions.\n\
|
||||
- debugger: Finds root causes, suspect changes, and validation paths. Best for bugs and regressions.\n\
|
||||
- implementer: Converts requirements into concrete implementation steps. Best for execution planning.\n\
|
||||
- tester: Designs validation and regression coverage. Best for test strategy.\n\
|
||||
- security: Reviews auth, data exposure, injection, dependency, and abuse risks. Best for sensitive changes.\n\
|
||||
Provide a clear, focused task description so the sub-agent knows exactly what to investigate.",
|
||||
)
|
||||
.parameters(ToolSchema {
|
||||
@ -187,7 +187,7 @@ fn register_call_sub_agent_tool(
|
||||
name: "role".into(),
|
||||
param_type: "string".into(),
|
||||
description: Some(
|
||||
"The sub-agent role to delegate to: researcher, analyst, or reviewer.".into(),
|
||||
"The sub-agent role to delegate to: researcher, analyst, reviewer, architect, debugger, implementer, tester, or security.".into(),
|
||||
),
|
||||
required: true,
|
||||
properties: None,
|
||||
@ -224,12 +224,7 @@ fn register_call_sub_agent_tool(
|
||||
.unwrap_or("")
|
||||
.to_owned();
|
||||
|
||||
let profile = match role.as_str() {
|
||||
"researcher" => researcher_profile(),
|
||||
"analyst" => analyst_profile(),
|
||||
"reviewer" => reviewer_profile(),
|
||||
_ => researcher_profile(),
|
||||
};
|
||||
let profile = profile_for_role_name(role.as_str());
|
||||
|
||||
let mut sub_request = captured_request.clone();
|
||||
sub_request.input = format!(
|
||||
@ -288,9 +283,12 @@ fn filter_tools_for_sub_agent(
|
||||
let Some(tools) = original_tools else {
|
||||
return Vec::new();
|
||||
};
|
||||
let allowed = allowed_tools
|
||||
.as_ref()
|
||||
.map(|list| list.iter().filter(|n| *n != "call_sub_agent").cloned().collect::<Vec<String>>());
|
||||
let allowed = allowed_tools.as_ref().map(|list| {
|
||||
list.iter()
|
||||
.filter(|n| *n != "call_sub_agent")
|
||||
.cloned()
|
||||
.collect::<Vec<String>>()
|
||||
});
|
||||
|
||||
match allowed {
|
||||
Some(allowed_list) if !allowed_list.is_empty() => tools
|
||||
@ -308,8 +306,7 @@ fn filter_tools_for_sub_agent(
|
||||
_ => tools
|
||||
.iter()
|
||||
.filter(|tool| {
|
||||
tool
|
||||
.get("function")
|
||||
tool.get("function")
|
||||
.and_then(|f| f.get("name"))
|
||||
.and_then(|v| v.as_str())
|
||||
.is_some_and(|name| name != "call_sub_agent")
|
||||
@ -317,4 +314,4 @@ fn filter_tools_for_sub_agent(
|
||||
.cloned()
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,10 +165,7 @@ impl ChatService {
|
||||
request: AiRequest,
|
||||
room_tools: ToolRegistry,
|
||||
) -> Result<ProcessResult> {
|
||||
let mut merged = self
|
||||
.tool_registry
|
||||
.clone()
|
||||
.unwrap_or_default();
|
||||
let mut merged = self.tool_registry.clone().unwrap_or_default();
|
||||
merged.merge(room_tools);
|
||||
|
||||
super::nonstreaming_execution::execute_process(
|
||||
@ -191,10 +188,7 @@ impl ChatService {
|
||||
on_chunk: StreamCallback,
|
||||
room_tools: ToolRegistry,
|
||||
) -> Result<StreamResult> {
|
||||
let mut merged = self
|
||||
.tool_registry
|
||||
.clone()
|
||||
.unwrap_or_default();
|
||||
let mut merged = self.tool_registry.clone().unwrap_or_default();
|
||||
merged.merge(room_tools);
|
||||
|
||||
super::streaming_execution::execute_process_stream(
|
||||
|
||||
@ -198,9 +198,7 @@ impl super::CompactService {
|
||||
.unwrap_or_else(|_| estimate_input.len() / 4);
|
||||
|
||||
let retain_count = Self::resolve_retain_count(config, estimated_tokens);
|
||||
if estimated_tokens >= config.token_threshold
|
||||
&& messages.len() > retain_count
|
||||
{
|
||||
if estimated_tokens >= config.token_threshold && messages.len() > retain_count {
|
||||
let split_index = messages.len().saturating_sub(retain_count);
|
||||
let (to_summarize, retained_messages) = messages.split_at(split_index);
|
||||
let from_seq = to_summarize
|
||||
|
||||
@ -2,11 +2,11 @@ use models::rooms::room_message::Model as RoomMessageModel;
|
||||
use models::users::user::{Column as UserCol, Entity as User};
|
||||
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
|
||||
|
||||
use crate::AgentError;
|
||||
use crate::client::call_with_params;
|
||||
use crate::client::types::ChatRequestMessage;
|
||||
use crate::compact::types::{CompactConfig, MessageSummary};
|
||||
use crate::tokent::{TokenUsage, count_message_text};
|
||||
use crate::AgentError;
|
||||
|
||||
const DEFAULT_MODEL_CONTEXT_LIMIT: usize = 128_000;
|
||||
const MODEL_INPUT_RATIO_NUMERATOR: usize = 85;
|
||||
@ -193,7 +193,10 @@ impl super::CompactService {
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok((final_summary, if has_usage { Some(total_usage) } else { None }))
|
||||
Ok((
|
||||
final_summary,
|
||||
if has_usage { Some(total_usage) } else { None },
|
||||
))
|
||||
}
|
||||
|
||||
async fn merge_summary_rounds(
|
||||
@ -230,11 +233,7 @@ impl super::CompactService {
|
||||
for pair_text in fitted_pairs {
|
||||
let prompt = self.build_prompt(kind, true, &pair_text, current_budget);
|
||||
let (summary, usage) = self
|
||||
.invoke_summary_prompt(
|
||||
&prompt,
|
||||
current_budget,
|
||||
Self::temperature_for(kind),
|
||||
)
|
||||
.invoke_summary_prompt(&prompt, current_budget, Self::temperature_for(kind))
|
||||
.await?;
|
||||
Self::accumulate_usage(total_usage, has_usage, usage);
|
||||
next_round.push(summary);
|
||||
@ -474,18 +473,16 @@ impl super::CompactService {
|
||||
}
|
||||
|
||||
fn safe_model_input_budget_from_limit(model_context_limit: Option<usize>) -> usize {
|
||||
let context_limit = model_context_limit.unwrap_or(DEFAULT_MODEL_CONTEXT_LIMIT).max(1);
|
||||
let context_limit = model_context_limit
|
||||
.unwrap_or(DEFAULT_MODEL_CONTEXT_LIMIT)
|
||||
.max(1);
|
||||
context_limit
|
||||
.saturating_mul(MODEL_INPUT_RATIO_NUMERATOR)
|
||||
.saturating_div(MODEL_INPUT_RATIO_DENOMINATOR)
|
||||
.max(1)
|
||||
}
|
||||
|
||||
fn accumulate_usage(
|
||||
total: &mut TokenUsage,
|
||||
has_usage: &mut bool,
|
||||
usage: Option<TokenUsage>,
|
||||
) {
|
||||
fn accumulate_usage(total: &mut TokenUsage, has_usage: &mut bool, usage: Option<TokenUsage>) {
|
||||
if let Some(usage) = usage {
|
||||
total.input_tokens += usage.input_tokens;
|
||||
total.output_tokens += usage.output_tokens;
|
||||
@ -500,7 +497,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn room_summary_uses_eighty_five_percent_input_budget() {
|
||||
assert_eq!(CompactService::safe_model_input_budget_from_limit(Some(1000)), 850);
|
||||
assert_eq!(
|
||||
CompactService::safe_model_input_budget_from_limit(Some(1000)),
|
||||
850
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -19,8 +19,8 @@ pub use billing::{
|
||||
initialize_user_billing, persist_billing_error, record_ai_usage, record_user_ai_usage,
|
||||
};
|
||||
pub use chat::{
|
||||
AgentExecutionProfile, AgentRole, AiContextSenderType, AiRequest, AiStreamChunk,
|
||||
ChatService, Mention, RoomMessageContext, StreamCallback,
|
||||
AgentExecutionProfile, AgentRole, AiContextSenderType, AiRequest, AiStreamChunk, ChatService,
|
||||
Mention, RoomMessageContext, StreamCallback,
|
||||
};
|
||||
pub use client::types::ChatRequestMessage;
|
||||
pub use client::{AiCallResponse, AiClientConfig, call_with_params, call_with_retry};
|
||||
|
||||
@ -177,12 +177,7 @@ impl ToolContext {
|
||||
return Err(existing);
|
||||
}
|
||||
|
||||
match current.compare_exchange(
|
||||
existing,
|
||||
next,
|
||||
Ordering::AcqRel,
|
||||
Ordering::Relaxed,
|
||||
) {
|
||||
match current.compare_exchange(existing, next, Ordering::AcqRel, Ordering::Relaxed) {
|
||||
Ok(_) => return Ok(()),
|
||||
Err(_) => continue,
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user