Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/abraxas-365_langchain-rust.json
T

1 line
21 KiB
JSON

{"repo": "abraxas-365/langchain-rust", "files": {"examples/llm_deepseek.rs": {"cfc709a71fee02083ced078252d73c1f76fe9849": {"t": 1785745828.1431844, "n": 645, "h": "f7ef0501f567783a", "c": "use langchain_rust::language_models::llm::LLM;\nuse langchain_rust::llm::Deepseek;\nuse langchain_rust::schemas::Message;\n\n#[tokio::main]\nasync fn main() {\n // Initialize the Deepseek client\n // Requires DEEPSEEK_API_KEY environment variable to be set\n let deepseek = Deepseek::new()\n .with_api_key(\"your_api_key\")\n .with_model(\"deepseek-chat\"); // Can use enum: DeepseekModel::DeepseekChat.to_string()\n\n // Generate a response\n let response = deepseek\n .generate(&[Message::new_human_message(\"Introduce the Great Wall\")])\n .await\n .unwrap();\n\n println!(\"Response: {}\", response.generation);\n}\n"}}, "examples/llm_chain_deepseek.rs": {"cfc709a71fee02083ced078252d73c1f76fe9849": {"t": 1785745828.1504097, "n": 1806, "h": "f302d0cf21689f37", "c": "use langchain_rust::{\n chain::{chain_trait::Chain, llm_chain::LLMChainBuilder},\n language_models::options::CallOptions,\n llm::{Deepseek, DeepseekModel},\n prompt::{PromptTemplate, TemplateFormat},\n prompt_args,\n};\nuse std::env;\n\n#[tokio::main]\nasync fn main() {\n // Get API key from environment variable\n let api_key =\n env::var(\"DEEPSEEK_API_KEY\").expect(\"DEEPSEEK_API_KEY environment variable must be set\");\n\n // Setup the Deepseek client with desired model and parameters\n let deepseek = Deepseek::new()\n .with_api_key(api_key)\n .with_model(DeepseekModel::DeepseekChat.to_string())\n .with_options(\n CallOptions::default()\n .with_max_tokens(800)\n .with_temperature(1.3), // Using recommended temperature for general conversation\n );\n\n // Create a prompt template\n let template = r#\"\n You are a helpful assistant that provides detailed information. \n \n User question: {question}\n \n Please provide a comprehensive answer:\n \"#;\n\n let prompt = PromptTemplate::new(\n template.to_owned(),\n vec![\"question\".to_owned()],\n TemplateFormat::FString,\n );\n\n // Create an LLMChain using the builder pattern\n let chain = LLMChainBuilder::new()\n .prompt(prompt)\n .llm(deepseek)\n .build()\n .unwrap();\n\n // Execute the chain with a question\n let inputs = prompt_args! {\n \"question\" => \"Explain the importance of quantum computing and its potential applications.\"\n };\n\n let result = chain.call(inputs).await.unwrap();\n\n println!(\n \"Question: Explain the importance of quantum computing and its potential applications.\"\n );\n println!(\"\\nDeepseek's response:\");\n println!(\"{}\", result.generation);\n}\n"}}, "src/llm/deepseek/client.rs": {"cfc709a71fee02083ced078252d73c1f76fe9849": {"t": 1785745829.414779, "n": 17469, "h": "b861c534a14681f1", "c": "use crate::{\n language_models::{llm::LLM, options::CallOptions, GenerateResult, LLMError, TokenUsage},\n llm::DeepseekError,\n schemas::{Message, StreamData},\n};\nuse async_trait::async_trait;\nuse futures::{Stream, StreamExt};\nuse reqwest::Client;\nuse serde_json::Value;\nuse std::{pin::Pin, str};\n\nuse super::models::{ApiResponse, DeepseekMessage, Payload, ResponseFormat};\n\npub enum DeepseekModel {\n DeepseekChat,\n DeepseekReasoner,\n}\n\nimpl ToString for DeepseekModel {\n fn to_string(&self) -> String {\n match self {\n DeepseekModel::DeepseekChat => \"deepseek-chat\".to_string(),\n DeepseekModel::DeepseekReasoner => \"deepseek-reasoner\".to_string(),\n }\n }\n}\n\n#[derive(Clone)]\npub struct Deepseek {\n model: String,\n options: CallOptions,\n api_key: String,\n base_url: String,\n json_mode: bool,\n include_reasoning: bool,\n}\n\nimpl Default for Deepseek {\n fn default() -> Self {\n Self::new()\n }\n}\n\nimpl Deepseek {\n pub fn new() -> Self {\n Self {\n model: DeepseekModel::DeepseekChat.to_string(),\n options: CallOptions::default(),\n api_key: std::env::var(\"DEEPSEEK_API_KEY\").unwrap_or_default(),\n base_url: \"https://api.deepseek.com\".to_string(),\n json_mode: false,\n include_reasoning: false,\n }\n }\n\n pub fn with_model<S: Into<String>>(mut self, model: S) -> Self {\n self.model = model.into();\n self\n }\n\n pub fn with_options(mut self, options: CallOptions) -> Self {\n self.options = options;\n self\n }\n\n pub fn with_api_key<S: Into<String>>(mut self, api_key: S) -> Self {\n self.api_key = api_key.into();\n self\n }\n\n pub fn with_base_url<S: Into<String>>(mut self, base_url: S) -> Self {\n self.base_url = base_url.into();\n self\n }\n\n pub fn with_json_mode(mut self, json_mode: bool) -> Self {\n self.json_mode = json_mode;\n self\n }\n\n pub fn with_include_reasoning(mut self, include_reasoning: bool) -> Self {\n self.include_reasoning = include_reasoning;\n self\n }\n\n async fn generate(&self, messages: &[Message]) -> Result<GenerateResult, LLMError> {\n let client = Client::new();\n let is_stream = self.options.streaming_func.is_some();\n\n let payload = self.build_payload(messages, is_stream);\n let res = client\n .post(&format!(\"{}/v1/chat/completions\", self.base_url))\n .header(\"Authorization\", format!(\"Bearer {}\", self.api_key))\n .header(\"Content-Type\", \"application/json\")\n .json(&payload)\n .send()\n .await?;\n\n let status = res.status().as_u16();\n\n let res = match status {\n 400 => Err(LLMError::DeepseekError(DeepseekError::InvalidFormatError(\n \"Invalid request format\".to_string(),\n ))),\n 401 => Err(LLMError::DeepseekError(DeepseekError::AuthenticationError(\n \"Invalid API Key\".to_string(),\n ))),\n 402 => Err(LLMError::DeepseekError(\n DeepseekError::InsufficientBalanceError(\"Insufficient balance\".to_string()),\n )),\n 422 => Err(LLMError::DeepseekError(\n DeepseekError::InvalidParametersError(\"Invalid parameters\".to_string()),\n )),\n 429 => Err(LLMError::DeepseekError(DeepseekError::RateLimitError(\n \"Rate limit reached\".to_string(),\n ))),\n 500 => Err(LLMError::DeepseekError(DeepseekError::ServerError(\n \"Server error\".to_string(),\n ))),\n 503 => Err(LLMError::DeepseekError(\n DeepseekError::ServerOverloadedError(\"Server overloaded\".to_string()),\n )),\n _ => Ok(res.json::<ApiResponse>().await?),\n }?;\n\n let choice = res.choices.first();\n\n let mut generation = choice\n .map(|c| c.message.content.clone())\n .unwrap_or_default();\n\n // If include_reasoning is enabled and the model is deepseek-reasoner,\n // append the reasoning content to the generation if available\n if self.include_reasoning && self.model == DeepseekModel::DeepseekReasoner.to_string() {\n if let Some(reasoning) = choice.and_then(|c| c.message.reasoning_content.clone()) {\n generation = format!(\"Reasoning:\\n{}\\n\\nAnswer:\\n{}\", reasoning, generation);\n }\n }\n\n let tokens = Some(TokenUsage {\n prompt_tokens: res.usage.prompt_tokens,\n completion_tokens: res.usage.completion_tokens,\n total_tokens: res.usage.total_tokens,\n });\n\n Ok(GenerateResult { tokens, generation })\n }\n\n fn build_payload(&self, messages: &[Message], stream: bool) -> Payload {\n let mut response_format = None;\n if self.json_mode {\n response_format = Some(ResponseFormat {\n format_type: \"json_object\".to_string(),\n });\n }\n\n let mut payload = Payload {\n model: self.model.clone(),\n messages: messages\n .iter()\n .map(DeepseekMessage::from_message)\n .collect::<Vec<_>>(),\n max_tokens: self.options.max_tokens,\n stream: None,\n temperature: self.options.temperature,\n top_p: self.options.top_p,\n frequency_penalty: None,\n presence_penalty: None,\n stop: self.options.stop_words.clone(),\n response_format,\n };\n\n if stream {\n payload.stream = Some(true);\n }\n\n // Apply frequency_penalty if it's in the options range\n if let Some(fp) = self.options.frequency_penalty {\n if fp >= -2.0 && fp <= 2.0 {\n payload.frequency_penalty = Some(fp);\n }\n }\n\n // Apply presence_penalty if it's in the options range\n if let Some(pp) = self.options.presence_penalty {\n if pp >= -2.0 && pp <= 2.0 {\n payload.presence_penalty = Some(pp);\n }\n }\n\n payload\n }\n\n fn parse_sse_chunk(chunk: &[u8]) -> Result<Vec<Value>, LLMError> {\n let text = str::from_utf8(chunk).map_err(|e| LLMError::ParsingError(e.to_string()))?;\n let mut values = Vec::new();\n\n for line in text.lines() {\n if line.starts_with(\"data: \") {\n let data = &line[6..];\n if data == \"[DONE]\" {\n continue;\n }\n let value: Value = serde_json::from_str(data).map_err(|e| {\n LLMError::ParsingError(format!(\"Failed to parse SSE data: {}\", e))\n })?;\n values.push(value);\n }\n }\n\n Ok(values)\n }\n}\n\n#[async_trait]\nimpl LLM for Deepseek {\n async fn generate(&self, messages: &[Message]) -> Result<GenerateResult, LLMError> {\n match &self.options.streaming_func {\n Some(func) => {\n let mut complete_response = String::new();\n let mut stream = self.stream(messages).await?;\n while let Some(data) = stream.next().await {\n match data {\n Ok(value) => {\n let mut func = func.lock().await;\n complete_response.push_str(&value.content);\n let _ = func(value.content).await;\n }\n Err(e) => return Err(e),\n }\n }\n let mut generate_result = GenerateResult::default();\n generate_result.generation = complete_response;\n Ok(generate_result)\n }\n None => self.generate(messages).await,\n }\n }\n\n async fn stream(\n &self,\n messages: &[Message],\n ) -> Result<Pin<Box<dyn Stream<Item = Result<StreamData, LLMError>> + Send>>, LLMError> {\n let client = Client::new();\n let payload = self.build_payload(messages, true);\n let request = client\n .post(&format!(\"{}/v1/chat/completions\", self.base_url))\n .header(\"Authorization\", format!(\"Bearer {}\", self.api_key))\n .header(\"Content-Type\", \"application/json\")\n .json(&payload)\n .build()?;\n\n let stream = client.execute(request).await?;\n let stream = stream.bytes_stream();\n\n let include_reasoning = self.include_reasoning;\n let is_reasoner = self.model == DeepseekModel::DeepseekReasoner.to_string();\n\n let processed_stream = stream\n .then(move |result| {\n async move {\n match result {\n Ok(bytes) => {\n let chunks = Self::parse_sse_chunk(&bytes)?;\n\n for chunk in chunks {\n if let Some(choices) =\n chunk.get(\"choices\").and_then(|c| c.as_array())\n {\n if let Some(choice) = choices.first() {\n if let Some(delta) = choice.get(\"delta\") {\n // Handle reasoning_content if it exists\n if include_reasoning && is_reasoner {\n if let Some(reasoning) = delta\n .get(\"reasoning_content\")\n .and_then(|c| c.as_str())\n {\n if !reasoning.is_empty() {\n let usage = if let Some(usage) =\n chunk.get(\"usage\")\n {\n Some(TokenUsage {\n prompt_tokens: usage\n .get(\"prompt_tokens\")\n .and_then(|t| t.as_u64())\n .unwrap_or(0)\n as u32,\n completion_tokens: usage\n .get(\"completion_tokens\")\n .and_then(|t| t.as_u64())\n .unwrap_or(0)\n as u32,\n total_tokens: usage\n .get(\"total_tokens\")\n .and_then(|t| t.as_u64())\n .unwrap_or(0)\n as u32,\n })\n } else {\n None\n };\n\n return Ok(StreamData::new(\n chunk.clone(),\n usage,\n format!(\"Reasoning: {}\", reasoning),\n ));\n }\n }\n }\n\n // Handle content as before\n if let Some(content) =\n delta.get(\"content\").and_then(|c| c.as_str())\n {\n if !content.is_empty() {\n let usage =\n if let Some(usage) = chunk.get(\"usage\") {\n Some(TokenUsage {\n prompt_tokens: usage\n .get(\"prompt_tokens\")\n .and_then(|t| t.as_u64())\n .unwrap_or(0)\n as u32,\n completion_tokens: usage\n .get(\"completion_tokens\")\n .and_then(|t| t.as_u64())\n .unwrap_or(0)\n as u32,\n total_tokens: usage\n .get(\"total_tokens\")\n .and_then(|t| t.as_u64())\n .unwrap_or(0)\n as u32,\n })\n } else {\n None\n };\n\n return Ok(StreamData::new(\n chunk.clone(),\n usage,\n content,\n ));\n }\n }\n }\n }\n }\n }\n\n // If we didn't return within the loop, return an empty stream data\n Ok(StreamData::new(Value::Null, None, \"\"))\n }\n Err(e) => Err(LLMError::OtherError(e.to_string())),\n }\n }\n })\n .filter_map(|result| async move {\n match result {\n Ok(data) if !data.content.is_empty() => Some(Ok(data)),\n Ok(_) => None,\n Err(e) => Some(Err(e)),\n }\n });\n\n Ok(Box::pin(processed_stream))\n }\n\n fn add_options(&mut self, options: CallOptions) {\n self.options = options;\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use crate::schemas::{Message, MessageType};\n\n #[tokio::test]\n #[ignore]\n async fn test_deepseek_generate() {\n let messages = vec![Message {\n content: \"Hello\".to_string(),\n message_type: MessageType::HumanMessage,\n id: Some(\"test_id\".to_string()),\n images: None,\n tool_calls: None,\n }];\n\n let client = Deepseek::new();\n let res = client.generate(&messages).await;\n assert!(res.is_ok());\n }\n\n #[tokio::test]\n #[ignore]\n async fn test_deepseek_stream() {\n let messages = vec![Message {\n content: \"Hello\".to_string(),\n message_type: MessageType::HumanMessage,\n id: Some(\"test_id\".to_string()),\n images: None,\n tool_calls: None,\n }];\n\n let client = Deepseek::new();\n let res = client.stream(&messages).await;\n assert!(res.is_ok());\n }\n\n #[tokio::test]\n #[ignore]\n async fn test_deepseek_reasoner() {\n let messages = vec![Message {\n content: \"9.11 and 9.8, which is greater?\".to_string(),\n message_type: MessageType::HumanMessage,\n id: Some(\"test_id\".to_string()),\n images: None,\n tool_calls: None,\n }];\n\n // Create a client with the DeepseekReasoner model and enable reasoning content\n let client = Deepseek::new()\n .with_model(DeepseekModel::DeepseekReasoner.to_string())\n .with_include_reasoning(true);\n\n let res = client.generate(&messages).await;\n assert!(res.is_ok());\n\n // The response will contain both the reasoning and answer content\n if let Ok(result) = res {\n println!(\"Generation result: {}\", result.generation);\n }\n }\n}\n"}}}}