Skip to main content

systemprompt_agent/services/a2a_server/processing/artifact/
mod.rs

1use anyhow::Result;
2use async_trait::async_trait;
3use std::sync::Arc;
4use systemprompt_identifiers::AgentName;
5use systemprompt_models::{AiProvider, CallToolResult, McpTool, RequestContext, ToolCall};
6
7use crate::models::a2a::Artifact;
8use crate::services::mcp::McpToA2aTransformer;
9
10#[async_trait]
11pub trait ToolProvider: Send + Sync {
12    async fn list_available_tools_for_agent(
13        &self,
14        agent_name: &AgentName,
15        context: &RequestContext,
16    ) -> Result<Vec<McpTool>>;
17}
18
19pub struct AiServiceToolProvider {
20    ai_service: Arc<dyn AiProvider>,
21}
22
23impl std::fmt::Debug for AiServiceToolProvider {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        f.debug_struct("AiServiceToolProvider")
26            .field("ai_service", &"<AiProvider>")
27            .finish()
28    }
29}
30
31impl AiServiceToolProvider {
32    pub fn new(ai_service: Arc<dyn AiProvider>) -> Self {
33        Self { ai_service }
34    }
35}
36
37#[async_trait]
38impl ToolProvider for AiServiceToolProvider {
39    async fn list_available_tools_for_agent(
40        &self,
41        agent_name: &AgentName,
42        context: &RequestContext,
43    ) -> Result<Vec<McpTool>> {
44        self.ai_service
45            .list_available_tools_for_agent(agent_name, context)
46            .await
47    }
48}
49
50#[derive(Debug)]
51pub struct ArtifactBuilder {
52    tool_calls: Vec<ToolCall>,
53    tool_results: Vec<CallToolResult>,
54    tools: Vec<McpTool>,
55    context_id: String,
56    task_id: String,
57}
58
59impl ArtifactBuilder {
60    pub fn new(
61        tool_calls: Vec<ToolCall>,
62        tool_results: Vec<CallToolResult>,
63        tools: Vec<McpTool>,
64        context_id: String,
65        task_id: String,
66    ) -> Self {
67        Self {
68            tool_calls,
69            tool_results,
70            tools,
71            context_id,
72            task_id,
73        }
74    }
75
76    fn get_output_schema(&self, tool_name: &str) -> Option<&serde_json::Value> {
77        self.tools
78            .iter()
79            .find(|t| t.name == tool_name)
80            .and_then(|t| t.output_schema.as_ref())
81    }
82
83    pub fn build_artifacts(&self) -> Result<Vec<Artifact>> {
84        let mut artifacts = Vec::new();
85
86        for (index, result) in self.tool_results.iter().enumerate() {
87            if let Some(structured_content) =
88                result.structured_content.as_ref().filter(|v| !v.is_null())
89            {
90                if let Some(tool_call) = self.tool_calls.get(index) {
91                    let output_schema = self.get_output_schema(&tool_call.name);
92
93                    let mut artifact = McpToA2aTransformer::transform_from_json(
94                        &tool_call.name,
95                        structured_content,
96                        output_schema,
97                        &self.context_id,
98                        &self.task_id,
99                        Some(&tool_call.arguments),
100                    )
101                    .map_err(|e| {
102                        anyhow::anyhow!("Tool '{}' artifact transform failed: {e}", tool_call.name)
103                    })?;
104
105                    artifact.metadata = artifact.metadata.with_execution_index(index);
106
107                    artifacts.push(artifact);
108                }
109            }
110        }
111
112        Ok(artifacts)
113    }
114}