recovery: change agent-providers as a directory
This commit is contained in:
1 parent
91b44fcc16
commit
339ca0f87e
9 files changed
+345
-1
No files matched your search
Submodule crates/agent-providers deleted from d360b650d8.
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "agent-providers"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
agent-core = { path = "../agent-core" }
|
||||||
|
agent-tools = { path = "../agent-tools" }
|
||||||
|
serde = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
async-trait = { workspace = true }
|
||||||
|
reqwest = { workspace = true }
|
||||||
|
tokio = { workspace = true }
|
||||||
|
dotenvy = { workspace = true }
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use agent_core::ReActAgent;
|
||||||
|
use agent_providers::ChatCompletionsProvider;
|
||||||
|
use agent_tools::{CurrentTimeTool, ReadFileTool};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
dotenvy::dotenv().ok();
|
||||||
|
let provider = Arc::new(ChatCompletionsProvider::from_env()?);
|
||||||
|
let agent = ReActAgent::builder(provider, std::env::var("MODEL").unwrap_or_default())
|
||||||
|
.system_prompt("你是小助手!")
|
||||||
|
.max_iterations(5)
|
||||||
|
.tool(Arc::new(CurrentTimeTool))
|
||||||
|
.tool(Arc::new(ReadFileTool))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let output = agent
|
||||||
|
.run("帮我看看 /home/aieyzi/Documents/prompts.txt 第二行里是什么, 你只看第二行。")
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
println!("-- after {} iteration(s) ---", output.iterations);
|
||||||
|
println!("{}", output.final_message.content.unwrap_or_default());
|
||||||
|
|
||||||
|
println!("-- full history ---");
|
||||||
|
for (i, m) in output.history.iter().enumerate() {
|
||||||
|
println!("[{}] {:?}: {:?}", i, m.role, m.content);
|
||||||
|
if let Some(calls) = &m.tool_calls {
|
||||||
|
for call in calls {
|
||||||
|
println!("{} -> {}({})", call.id, call.name, call.arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
use agent_core::AgentError;
|
||||||
|
use reqwest::Client;
|
||||||
|
|
||||||
|
use crate::chat_completions::types::{
|
||||||
|
ChatCompletionsErrorBody, ChatCompletionsErrorResponse, ChatCompletionsRequest,
|
||||||
|
ChatCompletionsResponse,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn send_chat(
|
||||||
|
client: &Client,
|
||||||
|
base_url: &str,
|
||||||
|
api_key: &str,
|
||||||
|
request: ChatCompletionsRequest,
|
||||||
|
) -> Result<ChatCompletionsResponse, AgentError> {
|
||||||
|
let url = format!("{}/chat/completions", base_url);
|
||||||
|
let response = client
|
||||||
|
.post(&url)
|
||||||
|
.header("Authorization", format!("Bearer {}", api_key))
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.json(&request)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let status = response.status();
|
||||||
|
if !status.is_success() {
|
||||||
|
let body = response.text().await.unwrap_or_default();
|
||||||
|
let message = serde_json::from_str::<ChatCompletionsErrorResponse>(&body)
|
||||||
|
.map(|e| e.error.message)
|
||||||
|
.unwrap_or(body);
|
||||||
|
return Err(AgentError::Api {
|
||||||
|
status: status.as_u16(),
|
||||||
|
message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let parsed: ChatCompletionsResponse = response.json().await?;
|
||||||
|
Ok(parsed)
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
use agent_core::{ChatRequest, ChatResponse, Message, Role, ToolCall};
|
||||||
|
|
||||||
|
use crate::chat_completions::types::{
|
||||||
|
ChatCompletionsFunctionCall, ChatCompletionsFunctionDef, ChatCompletionsMessage,
|
||||||
|
ChatCompletionsRequest, ChatCompletionsResponse, ChatCompletionsTool, ChatCompletionsToolCall,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl From<Message> for ChatCompletionsMessage {
|
||||||
|
fn from(message: Message) -> Self {
|
||||||
|
let role = match message.role {
|
||||||
|
Role::System => "system",
|
||||||
|
Role::User => "user",
|
||||||
|
Role::Assistant => "assistant",
|
||||||
|
Role::Tool => "tool",
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
role: role.into(),
|
||||||
|
content: message.content.into(),
|
||||||
|
tool_calls: message.tool_calls.map(|calls| {
|
||||||
|
calls
|
||||||
|
.into_iter()
|
||||||
|
.map(ChatCompletionsToolCall::from)
|
||||||
|
.collect()
|
||||||
|
}),
|
||||||
|
tool_call_id: message.tool_call_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ToolCall> for ChatCompletionsToolCall {
|
||||||
|
fn from(call: ToolCall) -> Self {
|
||||||
|
Self {
|
||||||
|
id: call.id,
|
||||||
|
r#type: "function".to_string(),
|
||||||
|
function: ChatCompletionsFunctionCall {
|
||||||
|
name: call.name,
|
||||||
|
arguments: call.arguments,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ChatCompletionsToolCall> for ToolCall {
|
||||||
|
fn from(call: ChatCompletionsToolCall) -> Self {
|
||||||
|
Self {
|
||||||
|
id: call.id,
|
||||||
|
name: call.function.name,
|
||||||
|
arguments: call.function.arguments,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ChatRequest> for ChatCompletionsRequest {
|
||||||
|
fn from(request: ChatRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
model: request.model.into(),
|
||||||
|
messages: request
|
||||||
|
.messages
|
||||||
|
.into_iter()
|
||||||
|
.map(ChatCompletionsMessage::from)
|
||||||
|
.collect(),
|
||||||
|
temperature: request.temperature,
|
||||||
|
max_tokens: request.max_tokens,
|
||||||
|
tools: request
|
||||||
|
.tools
|
||||||
|
.into_iter()
|
||||||
|
.map(|tool| ChatCompletionsTool {
|
||||||
|
r#type: "function",
|
||||||
|
function: ChatCompletionsFunctionDef {
|
||||||
|
name: tool.name,
|
||||||
|
description: tool.description.into(),
|
||||||
|
parameters: tool.parameters,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ChatCompletionsResponse> for ChatResponse {
|
||||||
|
fn from(response: ChatCompletionsResponse) -> Self {
|
||||||
|
let ChatCompletionsResponse {
|
||||||
|
model,
|
||||||
|
choices,
|
||||||
|
usage,
|
||||||
|
} = response;
|
||||||
|
|
||||||
|
let (content, tool_calls) = match choices.into_iter().next() {
|
||||||
|
Some(choice) => (
|
||||||
|
choice.message.content,
|
||||||
|
choice
|
||||||
|
.message
|
||||||
|
.tool_calls
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.map(ToolCall::from)
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
None => (None, Vec::new()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
model,
|
||||||
|
content,
|
||||||
|
prompt_tokens: usage.as_ref().map(|u| u.prompt_tokens),
|
||||||
|
completion_tokens: usage.as_ref().map(|u| u.completion_tokens),
|
||||||
|
tool_calls: tool_calls.into_iter().map(ToolCall::from).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
mod client;
|
||||||
|
mod convert;
|
||||||
|
mod types;
|
||||||
|
|
||||||
|
use agent_core::{AgentError, ChatRequest, ChatResponse, LlmProvider};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use reqwest::Client;
|
||||||
|
|
||||||
|
use crate::chat_completions::{
|
||||||
|
client::send_chat,
|
||||||
|
types::{ChatCompletionsRequest, ChatCompletionsResponse},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ChatCompletionsProvider {
|
||||||
|
api_key: String,
|
||||||
|
base_url: String,
|
||||||
|
client: Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChatCompletionsProvider {
|
||||||
|
pub fn new(base_url: impl Into<String>, api_key: impl Into<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
base_url: base_url.into(),
|
||||||
|
api_key: api_key.into(),
|
||||||
|
client: Client::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_env() -> Result<Self, AgentError> {
|
||||||
|
let base_url = std::env::var("BASE_URL")
|
||||||
|
.map_err(|_| AgentError::Provider("BASE_URL NOT SET".into()))?;
|
||||||
|
let api_key =
|
||||||
|
std::env::var("API_KEY").map_err(|_| AgentError::Provider("API_KEY NOT SET".into()))?;
|
||||||
|
Ok(Self::new(base_url, api_key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl LlmProvider for ChatCompletionsProvider {
|
||||||
|
async fn chat(&self, request: ChatRequest) -> Result<ChatResponse, AgentError> {
|
||||||
|
let req = ChatCompletionsRequest::from(request);
|
||||||
|
let resp = send_chat(&self.client, &self.base_url, &self.api_key, req).await?;
|
||||||
|
Ok(ChatResponse::from(resp))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct ChatCompletionsRequest {
|
||||||
|
pub model: String,
|
||||||
|
pub messages: Vec<ChatCompletionsMessage>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub temperature: Option<f32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub max_tokens: Option<u32>,
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub tools: Vec<ChatCompletionsTool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct ChatCompletionsMessage {
|
||||||
|
pub role: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub content: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub tool_calls: Option<Vec<ChatCompletionsToolCall>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub tool_call_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct ChatCompletionsTool {
|
||||||
|
pub r#type: &'static str,
|
||||||
|
pub function: ChatCompletionsFunctionDef,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct ChatCompletionsFunctionDef {
|
||||||
|
pub name: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub parameters: serde_json::Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ChatCompletionsToolCall {
|
||||||
|
pub id: String,
|
||||||
|
pub r#type: String,
|
||||||
|
pub function: ChatCompletionsFunctionCall,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ChatCompletionsFunctionCall {
|
||||||
|
pub name: String,
|
||||||
|
pub arguments: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ChatCompletionsResponse {
|
||||||
|
pub model: String,
|
||||||
|
pub choices: Vec<ChatCompletionsChoice>,
|
||||||
|
pub usage: Option<ChatCompletionsUsage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ChatCompletionsChoice {
|
||||||
|
pub message: ChatCompletionsResponseMessage,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ChatCompletionsResponseMessage {
|
||||||
|
pub content: Option<String>,
|
||||||
|
pub tool_calls: Option<Vec<ChatCompletionsToolCall>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ChatCompletionsUsage {
|
||||||
|
pub prompt_tokens: u32,
|
||||||
|
pub completion_tokens: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ChatCompletionsErrorResponse {
|
||||||
|
pub error: ChatCompletionsErrorBody,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ChatCompletionsErrorBody {
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
pub mod chat_completions;
|
||||||
|
pub use chat_completions::ChatCompletionsProvider;
|
||||||
|
|
||||||
|
pub fn add(left: u64, right: u64) -> u64 {
|
||||||
|
left + right
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
let result = add(2, 2);
|
||||||
|
assert_eq!(result, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user