diff --git a/docs/docs/home/overview.mdx b/docs/docs/home/overview.mdx index ffba1be97..5593c2ca6 100644 --- a/docs/docs/home/overview.mdx +++ b/docs/docs/home/overview.mdx @@ -21,7 +21,7 @@ Share your creations and ask questions in our [Discord](https://discord.gg/BTNBe ### Language features - **Python and Typescript support**: Plug-and-play BAML with other languages -- **JSON correction**: BAML fix bad JSON returned by LLMs (e.g. unquoted keys, newlines, comments, extra quotes, and more) +- **JSON correction**: BAML fixes bad JSON returned by LLMs (e.g. unquoted keys, newlines, comments, extra quotes, and more) - **Wide model support**: Ollama, Openai, Anthropic. Tested on small models like Llama2 - **Streaming**: Stream structured partial outputs - **Resilience and fallback features**: Add retries, redundancy, to your LLM calls diff --git a/docs/docs/syntax/client/client.mdx b/docs/docs/syntax/client/client.mdx index 7869178f5..ec01aa7a5 100644 --- a/docs/docs/syntax/client/client.mdx +++ b/docs/docs/syntax/client/client.mdx @@ -35,6 +35,7 @@ BAML ships with the following providers (you can can also write your own!): - `azure-openai` - `anthropic` - `ollama` + - `google-ai` - Composite client providers - `fallback` - `round-robin` @@ -122,16 +123,24 @@ Provider names: Accepts any options as defined by [Ollama SDK](https://github.com/ollama/ollama/blob/main/docs/api.md#generate-a-chat-completion). +```rust +client MyOllamaClient { + provider ollama + options { + model llama2 + } +} +``` #### Requirements -Make sure you disable CORS if you are trying to run ollama using the BAML VSCode playground: -1. in your terminal run `OLLAMA_ORIGINS="*" ollama serve` -2. Run `ollama run llama2` (or your model), and you should be good to go. + +1. For Ollama, in your terminal run `ollama serve` +2. In another window, run `ollama run llama2` (or your model), and you should be good to go. ```rust client MyClient { provider ollama options { - model mistral + model llama2 options { temperature 0 } @@ -139,6 +148,25 @@ client MyClient { } ``` +### Google + +Provider names: +- `google-ai` + +Accepts any options as defined by the [Gemini SDK](https://ai.google.dev/gemini-api/docs/api-overview). + + +```rust +client MyGoogleClient { + provider google-ai + options{ + model "gemini-1.5-pro-001" + api_key env.GOOGLE_API_KEY + } +} +``` + + ### Fallback The `baml-fallback` provider allows you to define a resilient client, by @@ -184,7 +212,7 @@ client MyClient { ``` ## Other providers -You can use the `openai` provider if the provider you're trying to use has the same ChatML response format. +You can use the `openai` provider if the provider you're trying to use has the same ChatML response format (i.e. HuggingFace via their Inference Endpoint or your own local endpoint) Some providers ask you to add a `base_url`, which you can do like this: diff --git a/docs/docs/syntax/type.mdx b/docs/docs/syntax/type.mdx index a94623cb6..b7e33be6e 100644 --- a/docs/docs/syntax/type.mdx +++ b/docs/docs/syntax/type.mdx @@ -32,6 +32,21 @@ title: Supported Types - **Syntax:** `null` +### ✅ Images + +You can use an image like this: + +```rust +function DescribeImage(myImg: image) -> string { + client GPT4Turbo + prompt #" + {{ _.role("user")}} + Describe the image in four words: + {{ myImg }} + "# +} +``` + ### ⚠️ bytes - Not yet supported. Use a `string[]` or `int[]` instead. @@ -65,20 +80,7 @@ temperature of 32 degrees Fahrenheit or cost of $100.00. the unit be part of the variable name. For example, `temperature_fahrenheit` and `cost_usd` (see [@alias](/docs/syntax/class#alias)). -### ✅ Images - -You can use an image like this: -```rust -function DescribeImage(myImg: image) -> string { - client GPT4Turbo - prompt #" - {{ _.role("user")}} - Describe the image in four words: - {{ myImg }} - "# -} -``` ## Composite/Structured Types diff --git a/engine/.turbo/daemon/d1f419c7a2c5692a-turbo.log.2024-06-10 b/engine/.turbo/daemon/d1f419c7a2c5692a-turbo.log.2024-06-10 new file mode 100644 index 000000000..ada5f528c --- /dev/null +++ b/engine/.turbo/daemon/d1f419c7a2c5692a-turbo.log.2024-06-10 @@ -0,0 +1,4 @@ +2024-06-10T23:15:35.616256Z WARN daemon_server: turborepo_lib::commands::daemon: daemon already running +2024-06-10T23:35:28.207259Z WARN daemon_server: turborepo_lib::commands::daemon: daemon already running +2024-06-10T23:48:30.344739Z WARN daemon_server: turborepo_lib::commands::daemon: daemon already running +2024-06-10T23:57:43.838177Z WARN daemon_server: turborepo_lib::commands::daemon: daemon already running diff --git a/engine/.turbo/daemon/d1f419c7a2c5692a-turbo.log.2024-06-11 b/engine/.turbo/daemon/d1f419c7a2c5692a-turbo.log.2024-06-11 new file mode 100644 index 000000000..0b3206c0f --- /dev/null +++ b/engine/.turbo/daemon/d1f419c7a2c5692a-turbo.log.2024-06-11 @@ -0,0 +1,2 @@ +2024-06-11T22:27:08.580345Z WARN daemon_server: turborepo_lib::commands::daemon: daemon already running +2024-06-11T22:40:18.568937Z WARN daemon_server: turborepo_lib::commands::daemon: daemon already running diff --git a/engine/Cargo.toml b/engine/Cargo.toml index d3ee9437e..5a4100f6e 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -50,7 +50,6 @@ strum = { version = "0.26.2", features = ["derive"] } strum_macros = "0.26.2" walkdir = "2.5.0" web-time = "1.1.0" - baml-types = { path = "baml-lib/baml-types" } internal-baml-codegen = { path = "language-client-codegen" } internal-baml-core = { path = "baml-lib/baml-core" } diff --git a/engine/baml-cli/src/init_command/clients.rs b/engine/baml-cli/src/init_command/clients.rs index 654588a30..7e5d7d1a2 100644 --- a/engine/baml-cli/src/init_command/clients.rs +++ b/engine/baml-cli/src/init_command/clients.rs @@ -136,3 +136,12 @@ fn anthropic_clients + AsRef>() -> Vec + AsRef>() -> Vec> { + vec![ClientConfig { + comment: None, + provider: "google-ai".into(), + name: "Gemini".into(), + params: vec![("model_name", "gemini"), ("api_key", "env.GOOGLE_API_KEY")], + }] +} diff --git a/engine/baml-lib/baml-core/src/validate/validation_pipeline/validations/clients.rs b/engine/baml-lib/baml-core/src/validate/validation_pipeline/validations/clients.rs index 720ecea39..a21475a26 100644 --- a/engine/baml-lib/baml-core/src/validate/validation_pipeline/validations/clients.rs +++ b/engine/baml-lib/baml-core/src/validate/validation_pipeline/validations/clients.rs @@ -19,6 +19,7 @@ pub(super) fn validate(ctx: &mut Context<'_>) { "round-robin", "baml-fallback", "fallback", + "google-ai", ]; let suggestions: Vec = allowed_providers diff --git a/engine/baml-runtime/.turbo/daemon/eeecc271d7c9d7d2-turbo.log.2024-06-10 b/engine/baml-runtime/.turbo/daemon/eeecc271d7c9d7d2-turbo.log.2024-06-10 new file mode 100644 index 000000000..e69de29bb diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/anthropic/anthropic_client.rs b/engine/baml-runtime/src/internal/llm_client/primitive/anthropic/anthropic_client.rs index c52a59a12..735906b74 100644 --- a/engine/baml-runtime/src/internal/llm_client/primitive/anthropic/anthropic_client.rs +++ b/engine/baml-runtime/src/internal/llm_client/primitive/anthropic/anthropic_client.rs @@ -17,7 +17,7 @@ use reqwest::Response; use crate::{ internal::llm_client::{ primitive::{ - anthropic::types::{AnthropicErrorResponse, AnthropicMessageResponse, StopReason}, + anthropic::types::{AnthropicMessageResponse, StopReason}, request::{make_parsed_request, make_request, RequestBuilder}, }, traits::{ @@ -35,6 +35,7 @@ use crate::RuntimeContext; use super::types::MessageChunk; +// stores properties required for making a post request to the API struct PostRequestProperities { default_role: String, base_url: String, @@ -45,6 +46,7 @@ struct PostRequestProperities { properties: HashMap, } +// represents client that interacts with the Anthropic API pub struct AnthropicClient { pub name: String, retry_policy: Option, @@ -56,6 +58,8 @@ pub struct AnthropicClient { client: reqwest::Client, } +// resolves/constructs PostRequestProperties from the client's options and runtime context, fleshing out the needed headers and parameters +// basically just reads the client's options and matches them to needed properties or defaults them fn resolve_properties( client: &ClientWalker, ctx: &RuntimeContext, @@ -131,6 +135,7 @@ fn resolve_properties( }) } +// getters for client info impl WithRetryPolicy for AnthropicClient { fn retry_policy_name(&self) -> Option<&str> { self.retry_policy.as_deref() @@ -149,6 +154,7 @@ impl WithClient for AnthropicClient { impl WithNoCompletion for AnthropicClient {} +// Manages processing response chunks from streaming response, and converting it into a structured response format impl SseResponseTrait for AnthropicClient { fn response_stream( &self, @@ -285,6 +291,7 @@ impl SseResponseTrait for AnthropicClient { } } +// handles streamign chat interactions, when sending prompt to API and processing response stream impl WithStreamChat for AnthropicClient { async fn stream_chat( &self, @@ -300,6 +307,7 @@ impl WithStreamChat for AnthropicClient { } } +// constructs base client and resolves properties based on context impl AnthropicClient { pub fn new(client: &ClientWalker, ctx: &RuntimeContext) -> Result { Ok(Self { @@ -324,6 +332,7 @@ impl AnthropicClient { } } +// how to build the HTTP request for requests impl RequestBuilder for AnthropicClient { fn http_client(&self) -> &reqwest::Client { &self.client @@ -377,6 +386,7 @@ impl RequestBuilder for AnthropicClient { if stream { body_obj.insert("stream".into(), true.into()); } + log::info!("Request body: {:#?}", body); req.json(&body) } @@ -447,17 +457,18 @@ impl WithChat for AnthropicClient { } } +// converts completion prompt into JSON body for request fn convert_completion_prompt_to_body(prompt: &String) -> HashMap { let mut map = HashMap::new(); map.insert("prompt".into(), json!(prompt)); map } +// converts chat prompt into JSON body for request fn convert_chat_prompt_to_body( prompt: &Vec, ) -> HashMap { let mut map = HashMap::new(); - log::debug!("converting chat prompt to body: {:#?}", prompt); if let Some(first) = prompt.get(0) { if first.role == "system" { @@ -511,6 +522,7 @@ fn convert_chat_prompt_to_body( return map; } +// converts chat message parts into JSON content fn convert_message_parts_to_content(parts: &Vec) -> serde_json::Value { if parts.len() == 1 { if let ChatMessagePart::Text(text) = &parts[0] { diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/google/google_client.rs b/engine/baml-runtime/src/internal/llm_client/primitive/google/google_client.rs new file mode 100644 index 000000000..82dc28cba --- /dev/null +++ b/engine/baml-runtime/src/internal/llm_client/primitive/google/google_client.rs @@ -0,0 +1,442 @@ +use crate::RuntimeContext; +use crate::{ + internal::llm_client::{ + primitive::{ + google::types::{FinishReason, GoogleResponse}, + request::{make_parsed_request, make_request, RequestBuilder}, + }, + traits::{ + SseResponseTrait, StreamResponse, WithChat, WithClient, WithNoCompletion, + WithRetryPolicy, WithStreamChat, + }, + ErrorCode, LLMCompleteResponse, LLMCompleteResponseMetadata, LLMErrorResponse, LLMResponse, + ModelFeatures, + }, + request::create_client, +}; +use anyhow::{Context, Result}; +use baml_types::BamlImage; +use eventsource_stream::Eventsource; +use futures::StreamExt; +use internal_baml_core::ir::ClientWalker; +use internal_baml_jinja::{ChatMessagePart, RenderContext_Client, RenderedChatMessage}; +use reqwest::Response; +use serde_json::json; +use std::collections::HashMap; +struct PostRequestProperities { + default_role: String, + api_key: Option, + headers: HashMap, + proxy_url: Option, + model_id: Option, + properties: HashMap, +} + +pub struct GoogleClient { + pub name: String, + pub client: reqwest::Client, + pub retry_policy: Option, + pub context: RenderContext_Client, + pub features: ModelFeatures, + properties: PostRequestProperities, +} + +fn resolve_properties( + client: &ClientWalker, + ctx: &RuntimeContext, +) -> Result { + let mut properties = (&client.item.elem.options) + .iter() + .map(|(k, v)| { + Ok(( + k.into(), + ctx.resolve_expression::(v) + .context(format!( + "client {} could not resolve options.{}", + client.name(), + k + ))?, + )) + }) + .collect::>>()?; + // this is a required field + + let default_role = properties + .remove("default_role") + .and_then(|v| v.as_str().map(|s| s.to_string())) + .unwrap_or_else(|| "user".to_string()); + + let api_key = properties + .remove("api_key") + .and_then(|v| v.as_str().map(|s| s.to_string())) + .or_else(|| ctx.env.get("GOOGLE_API_KEY").map(|s| s.to_string())); + + let model_id = properties + .remove("model") + .and_then(|v| v.as_str().map(|s| s.to_string())) + .or_else(|| Some("gemini-1.5-flash".to_string())); + + let headers = properties.remove("headers").map(|v| { + if let Some(v) = v.as_object() { + v.iter() + .map(|(k, v)| { + Ok(( + k.to_string(), + match v { + serde_json::Value::String(s) => s.to_string(), + _ => anyhow::bail!("Header '{k}' must be a string"), + }, + )) + }) + .collect::>>() + } else { + Ok(Default::default()) + } + }); + + let headers = match headers { + Some(h) => h?, + None => Default::default(), + }; + + Ok(PostRequestProperities { + default_role, + api_key, + headers, + properties, + model_id, + proxy_url: ctx.env.get("BOUNDARY_PROXY_URL").map(|s| s.to_string()), + }) +} + +impl WithRetryPolicy for GoogleClient { + fn retry_policy_name(&self) -> Option<&str> { + self.retry_policy.as_deref() + } +} + +impl WithClient for GoogleClient { + fn context(&self) -> &RenderContext_Client { + &self.context + } + + fn model_features(&self) -> &ModelFeatures { + &self.features + } +} + +impl WithNoCompletion for GoogleClient {} + +impl SseResponseTrait for GoogleClient { + fn response_stream( + &self, + resp: reqwest::Response, + prompt: &Vec, + system_start: web_time::SystemTime, + instant_start: web_time::Instant, + ) -> StreamResponse { + let prompt = prompt.clone(); + let client_name = self.context.name.clone(); + let model_id = self.properties.model_id.clone().unwrap_or_default(); + let params = self.properties.properties.clone(); + Ok(Box::pin( + resp.bytes_stream() + .eventsource() + .inspect(|event| log::info!("Received event: {:?}", event)) + .take_while(|event| { + std::future::ready(event.as_ref().is_ok_and(|e| e.data != "data: \n")) + }) + .map(|event| -> Result { + Ok(serde_json::from_str::(&event?.data)?) + }) + .scan( + Ok(LLMCompleteResponse { + client: client_name.clone(), + prompt: internal_baml_jinja::RenderedPrompt::Chat(prompt.clone()), + content: "".to_string(), + start_time: system_start, + latency: instant_start.elapsed(), + model: model_id, + invocation_params: params.clone(), + metadata: LLMCompleteResponseMetadata { + baml_is_complete: false, + finish_reason: None, + prompt_tokens: None, + output_tokens: None, + total_tokens: None, + }, + }), + move |accumulated: &mut Result, event| { + let Ok(ref mut inner) = accumulated else { + // halt the stream: the last stream event failed to parse + return std::future::ready(None); + }; + let event = match event { + Ok(event) => event, + Err(e) => { + return std::future::ready(Some(LLMResponse::LLMFailure( + LLMErrorResponse { + client: client_name.clone(), + model: if inner.model == "" { + None + } else { + Some(inner.model.clone()) + }, + prompt: internal_baml_jinja::RenderedPrompt::Chat( + prompt.clone(), + ), + start_time: system_start, + invocation_params: params.clone(), + latency: instant_start.elapsed(), + message: format!("Failed to parse event: {:#?}", e), + code: ErrorCode::Other(2), + }, + ))); + } + }; + + if let Some(choice) = event.candidates.get(0) { + if let Some(content) = choice.content.parts.get(0) { + inner.content += &content.text; + } + match choice.finish_reason.as_ref() { + Some(FinishReason::Stop) => { + inner.metadata.baml_is_complete = true; + inner.metadata.finish_reason = + Some(FinishReason::Stop.to_string()); + } + _ => (), + } + } + inner.latency = instant_start.elapsed(); + + std::future::ready(Some(LLMResponse::Success(inner.clone()))) + }, + ), + )) + } +} +// makes the request to the google client, on success it triggers the response_stream function to handle continuous rendering with the response object +impl WithStreamChat for GoogleClient { + async fn stream_chat( + &self, + ctx: &RuntimeContext, + prompt: &Vec, + ) -> StreamResponse { + //incomplete, streaming response object is returned + let (response, system_now, instant_now) = + match make_request(self, either::Either::Right(prompt), true).await { + Ok(v) => v, + Err(e) => return Err(e), + }; + self.response_stream(response, prompt, system_now, instant_now) + } +} + +impl GoogleClient { + pub fn new(client: &ClientWalker, ctx: &RuntimeContext) -> Result { + Ok(Self { + name: client.name().into(), + properties: resolve_properties(client, ctx)?, + context: RenderContext_Client { + name: client.name().into(), + provider: client.elem().provider.clone(), + }, + features: ModelFeatures { + chat: true, + completion: false, + anthropic_system_constraints: false, + }, + retry_policy: client + .elem() + .retry_policy_id + .as_ref() + .map(|s| s.to_string()), + client: create_client()?, + }) + } +} + +impl RequestBuilder for GoogleClient { + fn http_client(&self) -> &reqwest::Client { + &self.client + } + + fn build_request( + &self, + prompt: either::Either<&String, &Vec>, + stream: bool, + ) -> reqwest::RequestBuilder { + let mut should_stream = "generateContent?"; + if stream { + should_stream = "streamGenerateContent?alt=sse&"; + } + + let baml_original_url = format!( + "https://generativelanguage.googleapis.com/v1/models/{}:{}key={}", + self.properties.model_id.as_ref().unwrap_or(&"".to_string()), + should_stream, + self.properties + .api_key + .clone() + .unwrap_or_else(|| "".to_string()) + ); + + let mut req = self.client.post( + self.properties + .proxy_url + .as_ref() + .unwrap_or(&baml_original_url) + .clone(), + ); + + for (key, value) in &self.properties.headers { + req = req.header(key, value); + } + + req = req.header("baml-original-url", baml_original_url); + + let mut body = json!(self.properties.properties); + let body_obj = body.as_object_mut().unwrap(); + + match prompt { + either::Either::Left(prompt) => { + body_obj.extend(convert_completion_prompt_to_body(prompt)) + } + either::Either::Right(messages) => { + body_obj.extend(convert_chat_prompt_to_body(messages)) + } + } + + req.json(&body) + } + + fn invocation_params(&self) -> &HashMap { + &self.properties.properties + } +} + +impl WithChat for GoogleClient { + fn chat_options(&self, _ctx: &RuntimeContext) -> Result { + Ok(internal_baml_jinja::ChatOptions::new( + self.properties.default_role.clone(), + None, + )) + } + + async fn chat(&self, _ctx: &RuntimeContext, prompt: &Vec) -> LLMResponse { + //non-streaming, complete response is returned + let (response, system_now, instant_now) = + match make_parsed_request::(self, either::Either::Right(prompt), false) + .await + { + Ok(v) => v, + Err(e) => return e, + }; + + if response.candidates.len() != 1 { + return LLMResponse::LLMFailure(LLMErrorResponse { + client: self.context.name.to_string(), + model: None, + prompt: internal_baml_jinja::RenderedPrompt::Chat(prompt.clone()), + start_time: system_now, + invocation_params: self.properties.properties.clone(), + latency: instant_now.elapsed(), + message: format!( + "Expected exactly one content block, got {}", + response.candidates.len() + ), + code: ErrorCode::Other(200), + }); + } + + LLMResponse::Success(LLMCompleteResponse { + client: self.context.name.to_string(), + prompt: internal_baml_jinja::RenderedPrompt::Chat(prompt.clone()), + content: response.candidates[0].content.parts[0].text.clone(), + start_time: system_now, + latency: instant_now.elapsed(), + invocation_params: self.properties.properties.clone(), + model: self + .properties + .properties + .get("model") + .and_then(|v| v.as_str().map(|s| s.to_string())) + .or_else(|| _ctx.env.get("default model").map(|s| s.to_string())) + .unwrap_or_else(|| "".to_string()), + metadata: LLMCompleteResponseMetadata { + baml_is_complete: match response.candidates[0].finish_reason { + Some(FinishReason::Stop) => true, + _ => false, + }, + finish_reason: response.candidates[0] + .finish_reason + .as_ref() + .map(|r| serde_json::to_string(r).unwrap_or("".into())), + prompt_tokens: Some(response.usage_metadata.prompt_token_count), + output_tokens: Some(response.usage_metadata.candidates_token_count), + total_tokens: Some(response.usage_metadata.total_token_count), + }, + }) + } +} + +//simple, Map with key "prompt" and value of the prompt string +fn convert_completion_prompt_to_body(prompt: &String) -> HashMap { + let mut map = HashMap::new(); + let content = json!({ + "role": "user", + "parts": [{ + "text": prompt + }] + }); + map.insert("contents".into(), json!([content])); + map +} + +//list of chat messages into JSON body +fn convert_chat_prompt_to_body( + prompt: &Vec, +) -> HashMap { + let mut map = HashMap::new(); + + map.insert( + "contents".into(), + prompt + .iter() + .map(|m| { + json!({ + "role": m.role, + "parts": convert_message_parts_to_content(&m.parts) + }) + }) + .collect::(), + ); + + return map; +} + +fn convert_message_parts_to_content(parts: &Vec) -> serde_json::Value { + parts + .iter() + .map(|part| match part { + ChatMessagePart::Text(text) => json!({ + "text": text + }), + ChatMessagePart::Image(image) => match image { + BamlImage::Base64(image) => json!({ + "inlineDATA": { + "mimeType": image.media_type, + "data": image.base64 + } + }), + BamlImage::Url(image) => json!({ + "fileData": { + "type": "url", + "url": image.url + } + }), + }, + }) + .collect() +} diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/google/mod.rs b/engine/baml-runtime/src/internal/llm_client/primitive/google/mod.rs new file mode 100644 index 000000000..421322e53 --- /dev/null +++ b/engine/baml-runtime/src/internal/llm_client/primitive/google/mod.rs @@ -0,0 +1,4 @@ +mod google_client; +mod types; + +pub use google_client::GoogleClient; diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/google/types.rs b/engine/baml-runtime/src/internal/llm_client/primitive/google/types.rs new file mode 100644 index 000000000..85c59db95 --- /dev/null +++ b/engine/baml-runtime/src/internal/llm_client/primitive/google/types.rs @@ -0,0 +1,445 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct GoogleRequestBody { + pub contents: Vec, + pub tools: Option>, + pub safety_settings: Option, + pub generation_config: Option, + pub system_instruction: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct Tool { + pub function_declarations: Option>, + pub retrieval: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct FunctionDeclaration { + pub name: String, + pub description: Option, + pub parameters: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct Schema { + pub schema_type: Type, + pub format: String, + pub title: String, + pub description: String, + pub nullable: bool, + pub default: Option, + pub items: Option>, + pub min_items: Option, + pub max_items: Option, + pub enum_values: Option>, + pub properties: Option>, + pub required: Option>, + pub min_properties: Option, + pub max_properties: Option, + pub minimum: Option, + pub maximum: Option, + pub min_length: Option, + pub max_length: Option, + pub pattern: Option, + pub example: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +pub enum Value { + #[serde(rename = "NULL_VALUE")] + NullValue, + #[serde(rename = "NUMBER_VALUE")] + NumberValue(f64), + #[serde(rename = "STRING_VALUE")] + StringValue(String), + #[serde(rename = "BOOL_VALUE")] + BoolValue(bool), + #[serde(rename = "STRUCT_VALUE")] + StructValue(Struct), + #[serde(rename = "LIST_VALUE")] + ListValue(Vec), +} + +#[derive(Serialize, Deserialize, Debug)] +pub enum Type { + #[serde(rename = "STRING")] + String, + #[serde(rename = "NUMBER")] + Number, + #[serde(rename = "INTEGER")] + Integer, + #[serde(rename = "BOOLEAN")] + Boolean, + #[serde(rename = "OBJECT")] + Object, + #[serde(rename = "ARRAY")] + Array, + #[serde(rename = "TYPE_UNSPECIFIED")] + TypeUnspecified, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct Retrieval { + pub disable_attribution: bool, + pub vertex_ai_search: VertexAiSearch, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct VertexAiSearch { + pub datastore: String, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct SafetySetting { + pub category: HarmCategory, + pub threshold: HarmBlockThreshold, + pub method: HarmBlockMethod, +} + +#[derive(Serialize, Deserialize, Debug)] +pub enum HarmBlockThreshold { + #[serde(rename = "HARM_BLOCK_THRESHOLD_UNSPECIFIED")] + HarmBlockThresholdUnspecified, + #[serde(rename = "BLOCK_LOW_AND_ABOVE")] + BlockLowAndAbove, + #[serde(rename = "BLOCK_MEDIUM_AND_ABOVE")] + BlockMediumAndAbove, + #[serde(rename = "BLOCK_ONLY_HIGH")] + BlockOnlyHigh, + #[serde(rename = "BLOCK_NONE")] + BlockNone, +} + +#[derive(Serialize, Deserialize, Debug)] +pub enum HarmBlockMethod { + #[serde(rename = "harm_block_method_unspecified")] + HarmBlockMethodUnspecified, + #[serde(rename = "severity")] + Severity, + #[serde(rename = "probability")] + Probability, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct GenerationConfig { + pub stop_sequences: Option>, + pub response_mime_type: Option, + pub temperature: Option, + pub top_p: Option, + pub top_k: Option, + pub candidate_count: Option, + pub max_output_tokens: Option, + pub presence_penalty: Option, + pub frequency_penalty: Option, + pub response_schema: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct GoogleResponse { + pub candidates: Vec, + pub prompt_feedback: Option, + pub usage_metadata: UsageMetaData, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct PromptFeedback { + pub block_reason: BlockReason, + pub safety_ratings: Vec, + pub block_reason_message: String, +} + +#[derive(Serialize, Deserialize, Debug, strum_macros::Display)] +pub enum BlockReason { + #[serde(rename = "BLOCKED_REASON_UNSPECIFIED")] + BlockedReasonUnspecified, + #[serde(rename = "SAFETY")] + Safety, + #[serde(rename = "OTHER")] + Other, + #[serde(rename = "BLOCKLIST")] + Blocklist, + #[serde(rename = "PROHIBITED_CONTENT")] + ProhibitedContent, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct SafetyRating { + pub category: HarmCategory, + pub probability: HarmProbability, + pub probability_score: Option, + pub severity: Option, + pub severity_score: Option, + pub blocked: Option, +} + +#[derive(Serialize, Deserialize, Debug, strum_macros::Display)] +pub enum HarmCategory { + #[serde(rename = "HARM_CATEGORY_UNSPECIFIED")] + HarmCategoryUnspecified, + #[serde(rename = "HARM_CATEGORY_HATE_SPEECH")] + HarmCategoryHateSpeech, + #[serde(rename = "HARM_CATEGORY_DANGEROUS_CONTENT")] + HarmCategoryDangerousContent, + #[serde(rename = "HARM_CATEGORY_HARASSMENT")] + HarmCategoryHarassment, + #[serde(rename = "HARM_CATEGORY_SEXUALLY_EXPLICIT")] + HarmCategorySexuallyExplicit, +} + +#[derive(Serialize, Deserialize, Debug, strum_macros::Display)] +pub enum HarmProbability { + #[serde(rename = "HARM_PROBABILITY_UNSPECIFIED")] + HarmProbabilityUnspecified, + #[serde(rename = "NEGLIGIBLE")] + Negligible, + #[serde(rename = "LOW")] + Low, + #[serde(rename = "MEDIUM")] + Medium, + #[serde(rename = "HIGH")] + High, +} + +#[derive(Serialize, Deserialize, Debug, strum_macros::Display)] +pub enum HarmSeverity { + #[serde(rename = "HARM_SEVERITY_UNSPECIFIED")] + HarmSeverityUnspecified, + #[serde(rename = "HARM_SEVERITY_NEGLIGIBLE")] + HarmSeverityNegligible, + #[serde(rename = "HARM_SEVERITY_LOW")] + HarmSeverityLow, + #[serde(rename = "HARM_SEVERITY_MEDIUM")] + HarmSeverityMedium, + #[serde(rename = "HARM_SEVERITY_HIGH")] + HarmSeverityHigh, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct Candidate { + pub index: Option, + pub content: Content, + pub finish_reason: Option, + pub safety_ratings: Option>, + pub citation_metadata: Option, + pub grounding_metadata: Option, + pub finish_message: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct Content { + pub role: Option, + pub parts: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct Part { + pub text: String, + pub inline_data: Option, + pub file_data: Option, + pub function_call: Option, + pub function_response: Option, + pub video_metadata: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct Blob { + pub mime_type: String, + pub data: String, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct FileData { + pub mime_type: String, + pub file_uri: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct FunctionCall { + pub name: String, + pub args: Option>, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Struct { + pub fields: HashMap, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct FunctionResponse { + pub name: String, + pub response: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct VideoMetadata { + pub start_offset: Option, + pub end_offset: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Duration { + pub seconds: i64, + pub nanos: i32, +} + +#[derive(Serialize, Deserialize, Debug, strum_macros::Display)] +pub enum FinishReason { + #[serde(rename = "FINISH_REASON_UNSPECIFIED")] + FinishReasonUnspecified, + #[serde(rename = "STOP")] + Stop, + #[serde(rename = "MAX_TOKENS")] + MaxTokens, + #[serde(rename = "SAFETY")] + Safety, + #[serde(rename = "RECITATION")] + Recitation, + #[serde(rename = "OTHER")] + Other, + #[serde(rename = "BLOCKLIST")] + Blocklist, + #[serde(rename = "PROHIBITED_CONTENT")] + ProhibitedContent, + #[serde(rename = "SPII")] + Spii, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct CitationMetadata { + pub citations: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct Citation { + pub start_index: i32, + pub end_index: i32, + pub uri: String, + pub title: String, + pub license: String, + pub publication_date: Date, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Date { + pub year: i32, + pub month: i32, + pub day: i32, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct GroundingMetadata { + pub web_search_queries: Vec, + pub search_entry_point: SearchEntryPoint, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct SearchEntryPoint { + pub rendered_content: String, + pub sdk_blob: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct UsageMetaData { + pub prompt_token_count: u64, + pub candidates_token_count: u64, + pub total_token_count: u64, +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::Error; + + #[test] + fn test_deserialization() { + let data = r#" + { + "candidates": [ + { + "content": { + "role": "model", + "parts": [ + { + "text": "Dark fizz, cherry bright,\nTwenty-three flavors dance light,\nA Texan delight. \n" + } + ] + }, + "finishReason": "STOP", + "safetyRatings": [ + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE", + "probabilityScore": 0.04977345, + "severity": "HARM_SEVERITY_NEGLIGIBLE", + "severityScore": 0.06359858 + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE", + "probabilityScore": 0.06632687, + "severity": "HARM_SEVERITY_NEGLIGIBLE", + "severityScore": 0.103205055 + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE", + "probabilityScore": 0.06979492, + "severity": "HARM_SEVERITY_NEGLIGIBLE", + "severityScore": 0.058131594 + }, + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE", + "probabilityScore": 0.09285216, + "severity": "HARM_SEVERITY_NEGLIGIBLE", + "severityScore": 0.0992954 + } + ] + } + ], + "usageMetadata": { + "promptTokenCount": 8, + "candidatesTokenCount": 21, + "totalTokenCount": 29 + } + } + "#; + + let parsed: Result = serde_json::from_str(data); + + match parsed { + Ok(response) => println!("Parsed successfully: {:?}", response), + Err(e) => { + println!("Failed to parse: {}", e); + println!("Error line: {}", e.line()); + println!("Error column: {}", e.column()); + println!("Error cause: {:?}", e.classify()); + assert!(false, "Deserialization failed"); + } + } + } +} diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/mod.rs b/engine/baml-runtime/src/internal/llm_client/primitive/mod.rs index 20a5d52c5..5b6128293 100644 --- a/engine/baml-runtime/src/internal/llm_client/primitive/mod.rs +++ b/engine/baml-runtime/src/internal/llm_client/primitive/mod.rs @@ -9,7 +9,9 @@ use crate::{ RuntimeContext, }; -use self::{anthropic::AnthropicClient, openai::OpenAIClient, request::RequestBuilder}; +use self::{ + anthropic::AnthropicClient, google::GoogleClient, openai::OpenAIClient, request::RequestBuilder, +}; use super::{ orchestrator::{ @@ -22,12 +24,14 @@ use super::{ }; mod anthropic; +mod google; mod openai; pub(super) mod request; pub enum LLMPrimitiveProvider { OpenAI(OpenAIClient), Anthropic(AnthropicClient), + Google(GoogleClient), } macro_rules! match_llm_provider { @@ -36,6 +40,7 @@ macro_rules! match_llm_provider { match $self { LLMPrimitiveProvider::OpenAI(client) => client.$method($($args),*).await, LLMPrimitiveProvider::Anthropic(client) => client.$method($($args),*).await, + LLMPrimitiveProvider::Google(client) => client.$method($($args),*).await, } }; @@ -43,6 +48,7 @@ macro_rules! match_llm_provider { match $self { LLMPrimitiveProvider::OpenAI(client) => client.$method($($args),*), LLMPrimitiveProvider::Anthropic(client) => client.$method($($args),*), + LLMPrimitiveProvider::Google(client) => client.$method($($args),*), } }; } @@ -64,6 +70,7 @@ impl TryFrom<(&ClientWalker<'_>, &RuntimeContext)> for LLMPrimitiveProvider { "baml-ollama-chat" | "ollama" => { OpenAIClient::new_ollama(client, ctx).map(LLMPrimitiveProvider::OpenAI) } + "google-ai" => GoogleClient::new(client, ctx).map(LLMPrimitiveProvider::Google), other => { let options = [ "openai", @@ -141,6 +148,7 @@ impl std::fmt::Display for LLMPrimitiveProvider { match self { LLMPrimitiveProvider::OpenAI(_) => write!(f, "OpenAI"), LLMPrimitiveProvider::Anthropic(_) => write!(f, "Anthropic"), + LLMPrimitiveProvider::Google(_) => write!(f, "Google"), } } } diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/request.rs b/engine/baml-runtime/src/internal/llm_client/primitive/request.rs index fa57af8c6..c288da7d4 100644 --- a/engine/baml-runtime/src/internal/llm_client/primitive/request.rs +++ b/engine/baml-runtime/src/internal/llm_client/primitive/request.rs @@ -36,11 +36,7 @@ pub async fn make_request( let (system_now, instant_now) = (web_time::SystemTime::now(), web_time::Instant::now()); log::info!("Making request using client {}", client.context().name); - let req = match client - .build_request(prompt, stream) - .build() - .context("Failed to build request") - { + let req = match client.build_request(prompt, stream).build() { Ok(req) => req, Err(e) => { return Err(LLMResponse::LLMFailure(LLMErrorResponse { diff --git a/engine/baml-schema-wasm/.turbo/daemon/4dc1cf0b5f2dd46f-turbo.log.2024-06-10 b/engine/baml-schema-wasm/.turbo/daemon/4dc1cf0b5f2dd46f-turbo.log.2024-06-10 new file mode 100644 index 000000000..e69de29bb diff --git a/integ-tests/baml_src/clients.baml b/integ-tests/baml_src/clients.baml index 7872f35b7..57e7c9eec 100644 --- a/integ-tests/baml_src/clients.baml +++ b/integ-tests/baml_src/clients.baml @@ -67,6 +67,14 @@ client GPT35Azure { } } +client Gemini { + provider google-ai + options{ + model "gemini-1.5-pro-001" + api_key env.GOOGLE_API_KEY + } +} + client Claude { provider anthropic diff --git a/integ-tests/baml_src/test-files/providers/providers.baml b/integ-tests/baml_src/test-files/providers/providers.baml index f90ef2918..81466a2ab 100644 --- a/integ-tests/baml_src/test-files/providers/providers.baml +++ b/integ-tests/baml_src/test-files/providers/providers.baml @@ -1,4 +1,23 @@ +function TestAnthropic(input: string) -> string { + client Claude + prompt #" + Write a nice haiku about {{ input }} + "# +} + +function TestOpenAI(input: string) -> string { + client GPT35 + prompt #" + Write a nice haiku about {{ input }} + "# +} +function TestAzure(input: string) -> string { + client GPT35Azure + prompt #" + Write a nice haiku about {{ input }} + "# +} function TestOllama(input: string) -> string { client Ollama @@ -7,9 +26,19 @@ function TestOllama(input: string) -> string { "# } +function TestGemini(input: string) -> string { + client Gemini + prompt #" + Write a nice short story about {{ input }} + "# +} + + test TestProvider { - functions [TestOllama] + functions [TestAnthropic, TestOpenAI, TestAzure, TestOllama, TestGemini] args { - input "the moon" + input "Donkey kong and peanut butter" } } + + diff --git a/integ-tests/python/.gitignore b/integ-tests/python/.gitignore index 861f74350..2299a6acd 100644 --- a/integ-tests/python/.gitignore +++ b/integ-tests/python/.gitignore @@ -4,4 +4,5 @@ __pycache__/ *$py.class # C extensions -*.so \ No newline at end of file +*.so +.env \ No newline at end of file diff --git a/integ-tests/python/baml_client/client.py b/integ-tests/python/baml_client/client.py index 0266e1a69..c3aa4bb0a 100644 --- a/integ-tests/python/baml_client/client.py +++ b/integ-tests/python/baml_client/client.py @@ -869,6 +869,50 @@ async def PromptTestOpenAIChatNoSystem( mdl = create_model("PromptTestOpenAIChatNoSystemReturnType", inner=(str, ...)) return coerce(mdl, raw.parsed()) + async def TestAnthropic( + self, + input: str, + baml_options: BamlCallOptions = {}, + ) -> str: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + + raw = await self.__runtime.call_function( + "TestAnthropic", + { + "input": input, + }, + self.__ctx_manager.get(), + tb, + ) + mdl = create_model("TestAnthropicReturnType", inner=(str, ...)) + return coerce(mdl, raw.parsed()) + + async def TestAzure( + self, + input: str, + baml_options: BamlCallOptions = {}, + ) -> str: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + + raw = await self.__runtime.call_function( + "TestAzure", + { + "input": input, + }, + self.__ctx_manager.get(), + tb, + ) + mdl = create_model("TestAzureReturnType", inner=(str, ...)) + return coerce(mdl, raw.parsed()) + async def TestFallbackClient( self, @@ -1067,6 +1111,28 @@ async def TestFnNamedArgsSingleStringList( mdl = create_model("TestFnNamedArgsSingleStringListReturnType", inner=(str, ...)) return coerce(mdl, raw.parsed()) + async def TestGemini( + self, + input: str, + baml_options: BamlCallOptions = {}, + ) -> str: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + + raw = await self.__runtime.call_function( + "TestGemini", + { + "input": input, + }, + self.__ctx_manager.get(), + tb, + ) + mdl = create_model("TestGeminiReturnType", inner=(str, ...)) + return coerce(mdl, raw.parsed()) + async def TestImageInput( self, img: baml_py.Image, @@ -1133,6 +1199,28 @@ async def TestOllama( mdl = create_model("TestOllamaReturnType", inner=(str, ...)) return coerce(mdl, raw.parsed()) + async def TestOpenAI( + self, + input: str, + baml_options: BamlCallOptions = {}, + ) -> str: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + + raw = await self.__runtime.call_function( + "TestOpenAI", + { + "input": input, + }, + self.__ctx_manager.get(), + tb, + ) + mdl = create_model("TestOpenAIReturnType", inner=(str, ...)) + return coerce(mdl, raw.parsed()) + async def TestRetryConstant( self, @@ -2397,6 +2485,70 @@ def PromptTestOpenAIChatNoSystem( tb, ) + def TestAnthropic( + self, + input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlStream[Optional[str], str]: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + + raw = self.__runtime.stream_function( + "TestAnthropic", + { + "input": input, + }, + None, + self.__ctx_manager.get(), + tb, + ) + + mdl = create_model("TestAnthropicReturnType", inner=(str, ...)) + partial_mdl = create_model("TestAnthropicPartialReturnType", inner=(Optional[str], ...)) + + return baml_py.BamlStream[Optional[str], str]( + raw, + lambda x: coerce(partial_mdl, x), + lambda x: coerce(mdl, x), + self.__ctx_manager.get(), + tb, + ) + + def TestAzure( + self, + input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlStream[Optional[str], str]: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + + raw = self.__runtime.stream_function( + "TestAzure", + { + "input": input, + }, + None, + self.__ctx_manager.get(), + tb, + ) + + mdl = create_model("TestAzureReturnType", inner=(str, ...)) + partial_mdl = create_model("TestAzurePartialReturnType", inner=(Optional[str], ...)) + + return baml_py.BamlStream[Optional[str], str]( + raw, + lambda x: coerce(partial_mdl, x), + lambda x: coerce(mdl, x), + self.__ctx_manager.get(), + tb, + ) + def TestFallbackClient( self, @@ -2684,6 +2836,38 @@ def TestFnNamedArgsSingleStringList( tb, ) + def TestGemini( + self, + input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlStream[Optional[str], str]: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + + raw = self.__runtime.stream_function( + "TestGemini", + { + "input": input, + }, + None, + self.__ctx_manager.get(), + tb, + ) + + mdl = create_model("TestGeminiReturnType", inner=(str, ...)) + partial_mdl = create_model("TestGeminiPartialReturnType", inner=(Optional[str], ...)) + + return baml_py.BamlStream[Optional[str], str]( + raw, + lambda x: coerce(partial_mdl, x), + lambda x: coerce(mdl, x), + self.__ctx_manager.get(), + tb, + ) + def TestImageInput( self, img: baml_py.Image, @@ -2781,6 +2965,38 @@ def TestOllama( tb, ) + def TestOpenAI( + self, + input: str, + baml_options: BamlCallOptions = {}, + ) -> baml_py.BamlStream[Optional[str], str]: + __tb__ = baml_options.get("tb", None) + if __tb__ is not None: + tb = __tb__._tb + else: + tb = None + + raw = self.__runtime.stream_function( + "TestOpenAI", + { + "input": input, + }, + None, + self.__ctx_manager.get(), + tb, + ) + + mdl = create_model("TestOpenAIReturnType", inner=(str, ...)) + partial_mdl = create_model("TestOpenAIPartialReturnType", inner=(Optional[str], ...)) + + return baml_py.BamlStream[Optional[str], str]( + raw, + lambda x: coerce(partial_mdl, x), + lambda x: coerce(mdl, x), + self.__ctx_manager.get(), + tb, + ) + def TestRetryConstant( self, diff --git a/integ-tests/python/baml_client/inlinedbaml.py b/integ-tests/python/baml_client/inlinedbaml.py index 4910f87d2..1191eecd2 100644 --- a/integ-tests/python/baml_client/inlinedbaml.py +++ b/integ-tests/python/baml_client/inlinedbaml.py @@ -16,14 +16,14 @@ file_map = { - "clients.baml": "retry_policy Bar {\n max_retries 3\n strategy {\n type exponential_backoff\n }\n}\n\nretry_policy Foo {\n max_retries 3\n strategy {\n type constant_delay\n delay_ms 100\n }\n}\n\nclient GPT4 {\n provider baml-openai-chat\n options {\n model gpt-4\n api_key env.OPENAI_API_KEY\n }\n} \n\n\nclient GPT4o {\n provider baml-openai-chat\n options {\n model gpt-4o\n api_key env.OPENAI_API_KEY\n }\n} \n\n\nclient GPT4Turbo {\n retry_policy Bar\n provider baml-openai-chat\n options {\n model gpt-4-turbo\n api_key env.OPENAI_API_KEY\n }\n} \n\nclient GPT35 {\n provider baml-openai-chat\n options {\n model \"gpt-3.5-turbo\"\n api_key env.OPENAI_API_KEY\n }\n}\n\nclient Ollama {\n provider ollama\n options {\n model llama2\n api_key \"\"\n }\n}\n\nclient GPT35Azure {\n provider azure-openai\n options {\n resource_name \"west-us-azure-baml\"\n deployment_id \"gpt-35-turbo-default\"\n // base_url \"https://west-us-azure-baml.openai.azure.com/openai/deployments/gpt-35-turbo-default\"\n api_version \"2024-02-01\"\n api_key env.AZURE_OPENAI_API_KEY\n }\n}\n\n\nclient Claude {\n provider anthropic\n options {\n model claude-3-haiku-20240307\n api_key env.ANTHROPIC_API_KEY\n max_tokens 1000\n }\n}\n\nclient Resilient_SimpleSyntax {\n retry_policy Foo\n provider baml-fallback\n options {\n strategy [\n GPT4Turbo\n GPT35\n Lottery_SimpleSyntax\n ]\n }\n} \n \nclient Lottery_SimpleSyntax {\n provider baml-round-robin\n options {\n start 0\n strategy [\n GPT35\n Claude\n ]\n }\n}\n", + "clients.baml": "retry_policy Bar {\n max_retries 3\n strategy {\n type exponential_backoff\n }\n}\n\nretry_policy Foo {\n max_retries 3\n strategy {\n type constant_delay\n delay_ms 100\n }\n}\n\nclient GPT4 {\n provider baml-openai-chat\n options {\n model gpt-4\n api_key env.OPENAI_API_KEY\n }\n} \n\n\nclient GPT4o {\n provider baml-openai-chat\n options {\n model gpt-4o\n api_key env.OPENAI_API_KEY\n }\n} \n\n\nclient GPT4Turbo {\n retry_policy Bar\n provider baml-openai-chat\n options {\n model gpt-4-turbo\n api_key env.OPENAI_API_KEY\n }\n} \n\nclient GPT35 {\n provider baml-openai-chat\n options {\n model \"gpt-3.5-turbo\"\n api_key env.OPENAI_API_KEY\n }\n}\n\nclient Ollama {\n provider ollama\n options {\n model llama2\n api_key \"\"\n }\n}\n\nclient GPT35Azure {\n provider azure-openai\n options {\n resource_name \"west-us-azure-baml\"\n deployment_id \"gpt-35-turbo-default\"\n // base_url \"https://west-us-azure-baml.openai.azure.com/openai/deployments/gpt-35-turbo-default\"\n api_version \"2024-02-01\"\n api_key env.AZURE_OPENAI_API_KEY\n }\n}\n\nclient Gemini {\n provider google-ai\n options{\n model \"gemini-1.5-pro-001\"\n api_key env.GOOGLE_API_KEY\n }\n}\n\n\nclient Claude {\n provider anthropic\n options {\n model claude-3-haiku-20240307\n api_key env.ANTHROPIC_API_KEY\n max_tokens 1000\n }\n}\n\nclient Resilient_SimpleSyntax {\n retry_policy Foo\n provider baml-fallback\n options {\n strategy [\n GPT4Turbo\n GPT35\n Lottery_SimpleSyntax\n ]\n }\n} \n \nclient Lottery_SimpleSyntax {\n provider baml-round-robin\n options {\n start 0\n strategy [\n GPT35\n Claude\n ]\n }\n}\n", "fiddle-examples/chain-of-thought.baml": "class Email {\n subject string\n body string\n from_address string\n}\n\nenum OrderStatus {\n ORDERED\n SHIPPED\n DELIVERED\n CANCELLED\n}\n\nclass OrderInfo {\n order_status OrderStatus\n tracking_number string?\n estimated_arrival_date string?\n}\n\nfunction GetOrderInfo(email: Email) -> OrderInfo {\n client GPT4\n prompt #\"\n Given the email below:\n\n ```\n from: {{email.from_address}}\n Email Subject: {{email.subject}}\n Email Body: {{email.body}}\n ```\n\n Extract this info from the email in JSON format:\n {{ ctx.output_format }}\n\n Before you output the JSON, please explain your\n reasoning step-by-step. Here is an example on how to do this:\n 'If we think step by step we can see that ...\n therefore the output JSON is:\n {\n ... the json schema ...\n }'\n \"#\n}", "fiddle-examples/chat-roles.baml": "// This will be available as an enum in your Python and Typescript code.\nenum Category2 {\n Refund\n CancelOrder\n TechnicalSupport\n AccountIssue\n Question\n}\n\nfunction ClassifyMessage2(input: string) -> Category {\n client GPT4\n\n prompt #\"\n {{ _.role(\"system\") }}\n // You can use _.role(\"system\") to indicate that this text should be a system message\n\n Classify the following INPUT into ONE\n of the following categories:\n\n {{ ctx.output_format }}\n\n {{ _.role(\"user\") }}\n // And _.role(\"user\") to indicate that this text should be a user message\n\n INPUT: {{ input }}\n\n Response:\n \"#\n}", "fiddle-examples/classify-message.baml": "// This will be available as an enum in your Python and Typescript code.\nenum Category {\n Refund\n CancelOrder\n TechnicalSupport\n AccountIssue\n Question\n}\n\nfunction ClassifyMessage(input: string) -> Category {\n client GPT4\n\n prompt #\"\n Classify the following INPUT into ONE\n of the following categories:\n\n INPUT: {{ input }}\n\n {{ ctx.output_format }}\n\n Response:\n \"#\n}", "fiddle-examples/extract-names.baml": "function ExtractNames(input: string) -> string[] {\n client GPT4\n prompt #\"\n Extract the names from this INPUT:\n \n INPUT:\n ---\n {{ input }}\n ---\n\n {{ ctx.output_format }}\n\n Response:\n \"#\n}\n", "fiddle-examples/images/image.baml": "function DescribeImage(img: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"user\") }}\n\n\n Describe the image below in 5 words:\n {{ img }}\n \"#\n\n}\n\nclass FakeImage {\n url string\n}\n\nclass ClassWithImage {\n myImage image\n param2 string\n fake_image FakeImage\n}\n\n// chat role user present\nfunction DescribeImage2(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"user\") }}\n You should return 2 answers that answer the following commands.\n\n 1. Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n 2. Also tell me what's happening here in one sentence:\n {{ img2 }}\n \"#\n}\n\n// no chat role\nfunction DescribeImage3(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n Tell me also what's happening here in one sentence and relate it to the word {{ classWithImage.param2 }}:\n {{ img2 }}\n \"#\n}\n\n\n// system prompt and chat prompt\nfunction DescribeImage4(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"system\")}}\n\n Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n Tell me also what's happening here in one sentence and relate it to the word {{ classWithImage.param2 }}:\n {{ img2 }}\n \"#\n}", "fiddle-examples/symbol-tuning.baml": "enum Category3 {\n Refund @alias(\"k1\")\n @description(\"Customer wants to refund a product\")\n\n CancelOrder @alias(\"k2\")\n @description(\"Customer wants to cancel an order\")\n\n TechnicalSupport @alias(\"k3\")\n @description(\"Customer needs help with a technical issue unrelated to account creation or login\")\n\n AccountIssue @alias(\"k4\")\n @description(\"Specifically relates to account-login or account-creation\")\n\n Question @alias(\"k5\")\n @description(\"Customer has a question\")\n}\n\nfunction ClassifyMessage3(input: string) -> Category {\n client GPT4\n\n prompt #\"\n Classify the following INPUT into ONE\n of the following categories:\n\n INPUT: {{ input }}\n\n {{ ctx.output_format }}\n\n Response:\n \"#\n}", - "main.baml": "generator lang_python {\n output_type python/pydantic\n output_dir \"../python\"\n}\n\ngenerator lang_typescript {\n output_type typescript\n output_dir \"../typescript\"\n}\n\ngenerator lang_ruby {\n output_type ruby/sorbet\n output_dir \"../ruby\"\n}\n", + "main.baml": "generator lang_python {\n output_type python/pydantic\n output_dir \"../python\"\n}\n\ngenerator lang_typescript {\n output_type typescript\n output_dir \"../typescript\"\n}\n\n// generator lang_ruby {\n// output_type ruby/sorbet\n// output_dir \"../ruby\"\n// }\n", "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(\n User is confused\n )\n E @description(\n User is excited\n )\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", @@ -55,7 +55,7 @@ "test-files/functions/prompts/no-chat-messages.baml": "\n\nfunction PromptTestClaude(input: string) -> string {\n client Claude\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAI(input: string) -> string {\n client GPT35\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}", "test-files/functions/prompts/with-chat-messages.baml": "\nfunction PromptTestOpenAIChat(input: string) -> string {\n client GPT35\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAIChatNoSystem(input: string) -> string {\n client GPT35\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChat(input: string) -> string {\n client Claude\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChatNoSystem(input: string) -> string {\n client Claude\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\ntest PromptTestOpenAIChat {\n functions [PromptTestClaude, PromptTestOpenAI, PromptTestOpenAIChat, PromptTestOpenAIChatNoSystem, PromptTestClaudeChat, PromptTestClaudeChatNoSystem]\n args {\n input \"cats\"\n }\n}\n\ntest TestClaude {\n functions [PromptTestClaudeChatNoSystem]\n args {\n input \"lion\"\n }\n}", "test-files/functions/v2/basic.baml": "\n\nfunction ExtractResume2(resume: string) -> Resume {\n client GPT4\n prompt #\"\n {{ _.role('system') }}\n\n Extract the following information from the resume:\n\n Resume:\n <<<<\n {{ resume }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}\n\n\nclass WithReasoning {\n value string\n reasoning string @description(#\"\n Why the value is a good fit.\n \"#)\n}\n\n\nclass SearchParams {\n dateRange int? @description(#\"\n In ISO duration format, e.g. P1Y2M10D.\n \"#)\n location string[]\n jobTitle WithReasoning? @description(#\"\n An exact job title, not a general category.\n \"#)\n company WithReasoning? @description(#\"\n The exact name of the company, not a product or service.\n \"#)\n description WithReasoning[] @description(#\"\n Any specific projects or features the user is looking for.\n \"#)\n tags (Tag | string)[]\n}\n\nenum Tag {\n Security\n AI\n Blockchain\n}\n\nfunction GetQuery(query: string) -> SearchParams {\n client GPT4\n prompt #\"\n Extract the following information from the query:\n\n Query:\n <<<<\n {{ query }}\n <<<<\n\n OUTPUT_JSON_SCHEMA:\n {{ ctx.output_format }}\n\n Before OUTPUT_JSON_SCHEMA, list 5 intentions the user may have.\n --- EXAMPLES ---\n 1. \n 2. \n 3. \n 4. \n 5. \n\n {\n ... // OUTPUT_JSON_SCHEMA\n }\n \"#\n}\n\nclass RaysData {\n dataType DataType\n value Resume | Event\n}\n\nenum DataType {\n Resume\n Event\n}\n\nclass Event {\n title string\n date string\n location string\n description string\n}\n\nfunction GetDataType(text: string) -> RaysData {\n client GPT4\n prompt #\"\n Extract the relevant info.\n\n Text:\n <<<<\n {{ text }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}", - "test-files/providers/providers.baml": "\n\nfunction TestOllama(input: string) -> string {\n client Ollama\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\ntest TestProvider {\n functions [TestOllama]\n args {\n input \"the moon\"\n }\n}\n", + "test-files/providers/providers.baml": "function TestAnthropic(input: string) -> string {\n client Claude\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOpenAI(input: string) -> string {\n client GPT35\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestAzure(input: string) -> string {\n client GPT35Azure\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOllama(input: string) -> string {\n client Ollama\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestGemini(input: string) -> string {\n client Gemini\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\n\ntest TestProvider {\n functions [TestAnthropic, TestOpenAI, TestAzure, TestOllama, TestGemini]\n args {\n input \"Donkey kong and peanut butter\"\n }\n}\n\n\n", "test-files/strategies/fallback.baml": "\nclient FaultyClient {\n provider openai\n options {\n model unknown-model\n api_key env.OPENAI_API_KEY\n }\n}\n\n\nclient FallbackClient {\n provider fallback\n options {\n // first 2 clients are expected to fail.\n strategy [\n FaultyClient,\n RetryClientConstant,\n GPT35\n ]\n }\n}\n\nfunction TestFallbackClient() -> string {\n client FallbackClient\n // TODO make it return the client name instead\n prompt #\"\n Say a haiku about mexico.\n \"#\n}", "test-files/strategies/retry.baml": "\nretry_policy Exponential {\n max_retries 3\n strategy {\n type exponential_backoff\n }\n}\n\nretry_policy Constant {\n max_retries 3\n strategy {\n type constant_delay\n delay_ms 100\n }\n}\n\nclient RetryClientConstant {\n provider openai\n retry_policy Constant\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blah\"\n }\n}\n\nclient RetryClientExponential {\n provider openai\n retry_policy Exponential\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blahh\"\n }\n}\n\nfunction TestRetryConstant() -> string {\n client RetryClientConstant\n prompt #\"\n Say a haiku\n \"#\n}\n\nfunction TestRetryExponential() -> string {\n client RetryClientExponential\n prompt #\"\n Say a haiku\n \"#\n}\n", "test-files/strategies/roundrobin.baml": "", diff --git a/integ-tests/python/poetry.lock b/integ-tests/python/poetry.lock index a20dee2da..8720de6ce 100644 --- a/integ-tests/python/poetry.lock +++ b/integ-tests/python/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "annotated-types" @@ -52,24 +52,24 @@ files = [ [[package]] name = "maturin" -version = "1.5.1" -description = "Build and publish crates with pyo3, rust-cpython and cffi bindings as well as rust binaries as python packages" +version = "1.6.0" +description = "Build and publish crates with pyo3, cffi and uniffi bindings as well as rust binaries as python packages" optional = false python-versions = ">=3.7" files = [ - {file = "maturin-1.5.1-py3-none-linux_armv6l.whl", hash = "sha256:589e9b7024007e130b136ba6f1c2c8393a87e42cf968d12852913ab1e3c69ed3"}, - {file = "maturin-1.5.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a1abda07093b3c8ef897626166c02ed64e3e446c48460b28efb51833abf89cbb"}, - {file = "maturin-1.5.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:48a1fbbdc2514525f27d6d339ab97b098ede28759f8593d110c89cc07bbe40ed"}, - {file = "maturin-1.5.1-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:96d96b1fa3a165db9ca539f764d31da8ebc92e31ca3a1dd6ccd50008d222bd96"}, - {file = "maturin-1.5.1-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:786bf36a98c4e27cbebb1dc8e432c1bcbbb59e7a9719592cbb89e46a0ccd5bcc"}, - {file = "maturin-1.5.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:d821b37da759884ad09cfee4cd9deac10f4132744cc66e4d9190a1972233bc83"}, - {file = "maturin-1.5.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:62133bf690555bbc8cc6b1c18a0c57b0ab2b4d68d3fcd320eb16df941563fe06"}, - {file = "maturin-1.5.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:6bff165252b1fcc887679ddf7b71b5cc024327ba96ea893133be38c0ed38f163"}, - {file = "maturin-1.5.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c42a95466ffc3de0a3940cd20c57cf0c44fe5ea679375d73422afbb00236c64"}, - {file = "maturin-1.5.1-py3-none-win32.whl", hash = "sha256:d09538b4aa0da4b59fd47cb429003b45bfd5d801714adf1db2511bf8bdea532f"}, - {file = "maturin-1.5.1-py3-none-win_amd64.whl", hash = "sha256:a3db9054222ac79275e082b21cfd234b8e036714a4ff227a0a28f6a3ffa3744d"}, - {file = "maturin-1.5.1-py3-none-win_arm64.whl", hash = "sha256:acf528e51413f6ae489473d64116d8c83f140386349004949d29137c16a82193"}, - {file = "maturin-1.5.1.tar.gz", hash = "sha256:3dd834ece80edb866af18cbd4635e0ecac40139c726428d5f1849ae154b26dca"}, + {file = "maturin-1.6.0-py3-none-linux_armv6l.whl", hash = "sha256:d8620970bd0b6a0acb99dbd0b1c2ebb7a69909d25f6023bdff9635a39001aa51"}, + {file = "maturin-1.6.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:bd85edcb1b8e2bcddc1b7d16ce58ce00a66aa80c422745c8ad9e132ac40d4b48"}, + {file = "maturin-1.6.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:337899784955934dd67b30497d1dd5fab22da89f60bb079dbaf2eaa446b97a10"}, + {file = "maturin-1.6.0-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:dbbbf25dc3c207b0a7bd4f3aea1df33d4f22b8508592796a6f36f4d8ed216db0"}, + {file = "maturin-1.6.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:d92b045e90ed919a8a2520dda64e3f384e5e746ea51e1498cc6ac3e9e5c76054"}, + {file = "maturin-1.6.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:d67ca8dc7f3b2314bd3bf83c4de52645e220ee312fd526e53acc6a735f233fad"}, + {file = "maturin-1.6.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:aa4eb7dca7d246b466392f21016f67ff09a9aff2305fa714ca25a2344e4639e7"}, + {file = "maturin-1.6.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:16ef860df20028618b5a064da06b02c1c47acba064a4d25aaf84662a459ec599"}, + {file = "maturin-1.6.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e931c92037128ade49cd26dd040d9c46ad8092d8170cc44f5c3a0b4a052d576"}, + {file = "maturin-1.6.0-py3-none-win32.whl", hash = "sha256:c87d1a7596c42b589099adb831343a56e02373588366e4cede96cbdf8bd68f9d"}, + {file = "maturin-1.6.0-py3-none-win_amd64.whl", hash = "sha256:a2a2436628c36d98dabd79b52256df7e12fc4fd1b122984d9373fdf918fd4609"}, + {file = "maturin-1.6.0-py3-none-win_arm64.whl", hash = "sha256:50133965e52d8b5b969381fee3fde111ae2383905cdaba7650f256e08ccddcd4"}, + {file = "maturin-1.6.0.tar.gz", hash = "sha256:b955025c24c8babc808db49e0ff90db8b4b1320dcc16b14eb26132841737230d"}, ] [package.dependencies] @@ -77,17 +77,17 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [package.extras] patchelf = ["patchelf"] -zig = ["ziglang (>=0.10.0,<0.11.0)"] +zig = ["ziglang (>=0.10.0,<0.13.0)"] [[package]] name = "packaging" -version = "24.0" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] @@ -107,18 +107,18 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pydantic" -version = "2.7.2" +version = "2.7.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.7.2-py3-none-any.whl", hash = "sha256:834ab954175f94e6e68258537dc49402c4a5e9d0409b9f1b86b7e934a8372de7"}, - {file = "pydantic-2.7.2.tar.gz", hash = "sha256:71b2945998f9c9b7919a45bde9a50397b289937d215ae141c1d0903ba7149fd7"}, + {file = "pydantic-2.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0"}, + {file = "pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.18.3" +pydantic-core = "2.18.4" typing-extensions = ">=4.6.1" [package.extras] @@ -126,90 +126,90 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.18.3" +version = "2.18.4" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.18.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:744697428fcdec6be5670460b578161d1ffe34743a5c15656be7ea82b008197c"}, - {file = "pydantic_core-2.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b40c05ced1ba4218b14986fe6f283d22e1ae2ff4c8e28881a70fb81fbfcda7"}, - {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a9a75622357076efb6b311983ff190fbfb3c12fc3a853122b34d3d358126c"}, - {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2e253af04ceaebde8eb201eb3f3e3e7e390f2d275a88300d6a1959d710539e2"}, - {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:855ec66589c68aa367d989da5c4755bb74ee92ccad4fdb6af942c3612c067e34"}, - {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d3e42bb54e7e9d72c13ce112e02eb1b3b55681ee948d748842171201a03a98a"}, - {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6ac9ffccc9d2e69d9fba841441d4259cb668ac180e51b30d3632cd7abca2b9b"}, - {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c56eca1686539fa0c9bda992e7bd6a37583f20083c37590413381acfc5f192d6"}, - {file = "pydantic_core-2.18.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:17954d784bf8abfc0ec2a633108207ebc4fa2df1a0e4c0c3ccbaa9bb01d2c426"}, - {file = "pydantic_core-2.18.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:98ed737567d8f2ecd54f7c8d4f8572ca7c7921ede93a2e52939416170d357812"}, - {file = "pydantic_core-2.18.3-cp310-none-win32.whl", hash = "sha256:9f9e04afebd3ed8c15d67a564ed0a34b54e52136c6d40d14c5547b238390e779"}, - {file = "pydantic_core-2.18.3-cp310-none-win_amd64.whl", hash = "sha256:45e4ffbae34f7ae30d0047697e724e534a7ec0a82ef9994b7913a412c21462a0"}, - {file = "pydantic_core-2.18.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b9ebe8231726c49518b16b237b9fe0d7d361dd221302af511a83d4ada01183ab"}, - {file = "pydantic_core-2.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b8e20e15d18bf7dbb453be78a2d858f946f5cdf06c5072453dace00ab652e2b2"}, - {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0d9ff283cd3459fa0bf9b0256a2b6f01ac1ff9ffb034e24457b9035f75587cb"}, - {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f7ef5f0ebb77ba24c9970da18b771711edc5feaf00c10b18461e0f5f5949231"}, - {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73038d66614d2e5cde30435b5afdced2b473b4c77d4ca3a8624dd3e41a9c19be"}, - {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6afd5c867a74c4d314c557b5ea9520183fadfbd1df4c2d6e09fd0d990ce412cd"}, - {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd7df92f28d351bb9f12470f4c533cf03d1b52ec5a6e5c58c65b183055a60106"}, - {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:80aea0ffeb1049336043d07799eace1c9602519fb3192916ff525b0287b2b1e4"}, - {file = "pydantic_core-2.18.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaee40f25bba38132e655ffa3d1998a6d576ba7cf81deff8bfa189fb43fd2bbe"}, - {file = "pydantic_core-2.18.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9128089da8f4fe73f7a91973895ebf2502539d627891a14034e45fb9e707e26d"}, - {file = "pydantic_core-2.18.3-cp311-none-win32.whl", hash = "sha256:fec02527e1e03257aa25b1a4dcbe697b40a22f1229f5d026503e8b7ff6d2eda7"}, - {file = "pydantic_core-2.18.3-cp311-none-win_amd64.whl", hash = "sha256:58ff8631dbab6c7c982e6425da8347108449321f61fe427c52ddfadd66642af7"}, - {file = "pydantic_core-2.18.3-cp311-none-win_arm64.whl", hash = "sha256:3fc1c7f67f34c6c2ef9c213e0f2a351797cda98249d9ca56a70ce4ebcaba45f4"}, - {file = "pydantic_core-2.18.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f0928cde2ae416a2d1ebe6dee324709c6f73e93494d8c7aea92df99aab1fc40f"}, - {file = "pydantic_core-2.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bee9bb305a562f8b9271855afb6ce00223f545de3d68560b3c1649c7c5295e9"}, - {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e862823be114387257dacbfa7d78547165a85d7add33b446ca4f4fae92c7ff5c"}, - {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a36f78674cbddc165abab0df961b5f96b14461d05feec5e1f78da58808b97e7"}, - {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba905d184f62e7ddbb7a5a751d8a5c805463511c7b08d1aca4a3e8c11f2e5048"}, - {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fdd362f6a586e681ff86550b2379e532fee63c52def1c666887956748eaa326"}, - {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b214b7ee3bd3b865e963dbed0f8bc5375f49449d70e8d407b567af3222aae4"}, - {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:691018785779766127f531674fa82bb368df5b36b461622b12e176c18e119022"}, - {file = "pydantic_core-2.18.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:60e4c625e6f7155d7d0dcac151edf5858102bc61bf959d04469ca6ee4e8381bd"}, - {file = "pydantic_core-2.18.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4e651e47d981c1b701dcc74ab8fec5a60a5b004650416b4abbef13db23bc7be"}, - {file = "pydantic_core-2.18.3-cp312-none-win32.whl", hash = "sha256:ffecbb5edb7f5ffae13599aec33b735e9e4c7676ca1633c60f2c606beb17efc5"}, - {file = "pydantic_core-2.18.3-cp312-none-win_amd64.whl", hash = "sha256:2c8333f6e934733483c7eddffdb094c143b9463d2af7e6bd85ebcb2d4a1b82c6"}, - {file = "pydantic_core-2.18.3-cp312-none-win_arm64.whl", hash = "sha256:7a20dded653e516a4655f4c98e97ccafb13753987434fe7cf044aa25f5b7d417"}, - {file = "pydantic_core-2.18.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:eecf63195be644b0396f972c82598cd15693550f0ff236dcf7ab92e2eb6d3522"}, - {file = "pydantic_core-2.18.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c44efdd3b6125419c28821590d7ec891c9cb0dff33a7a78d9d5c8b6f66b9702"}, - {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e59fca51ffbdd1638b3856779342ed69bcecb8484c1d4b8bdb237d0eb5a45e2"}, - {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70cf099197d6b98953468461d753563b28e73cf1eade2ffe069675d2657ed1d5"}, - {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63081a49dddc6124754b32a3774331467bfc3d2bd5ff8f10df36a95602560361"}, - {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:370059b7883485c9edb9655355ff46d912f4b03b009d929220d9294c7fd9fd60"}, - {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a64faeedfd8254f05f5cf6fc755023a7e1606af3959cfc1a9285744cc711044"}, - {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19d2e725de0f90d8671f89e420d36c3dd97639b98145e42fcc0e1f6d492a46dc"}, - {file = "pydantic_core-2.18.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:67bc078025d70ec5aefe6200ef094576c9d86bd36982df1301c758a9fff7d7f4"}, - {file = "pydantic_core-2.18.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:adf952c3f4100e203cbaf8e0c907c835d3e28f9041474e52b651761dc248a3c0"}, - {file = "pydantic_core-2.18.3-cp38-none-win32.whl", hash = "sha256:9a46795b1f3beb167eaee91736d5d17ac3a994bf2215a996aed825a45f897558"}, - {file = "pydantic_core-2.18.3-cp38-none-win_amd64.whl", hash = "sha256:200ad4e3133cb99ed82342a101a5abf3d924722e71cd581cc113fe828f727fbc"}, - {file = "pydantic_core-2.18.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:304378b7bf92206036c8ddd83a2ba7b7d1a5b425acafff637172a3aa72ad7083"}, - {file = "pydantic_core-2.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c826870b277143e701c9ccf34ebc33ddb4d072612683a044e7cce2d52f6c3fef"}, - {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e201935d282707394f3668380e41ccf25b5794d1b131cdd96b07f615a33ca4b1"}, - {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5560dda746c44b48bf82b3d191d74fe8efc5686a9ef18e69bdabccbbb9ad9442"}, - {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b32c2a1f8032570842257e4c19288eba9a2bba4712af542327de9a1204faff8"}, - {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:929c24e9dea3990bc8bcd27c5f2d3916c0c86f5511d2caa69e0d5290115344a9"}, - {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a8376fef60790152564b0eab376b3e23dd6e54f29d84aad46f7b264ecca943"}, - {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dccf3ef1400390ddd1fb55bf0632209d39140552d068ee5ac45553b556780e06"}, - {file = "pydantic_core-2.18.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:41dbdcb0c7252b58fa931fec47937edb422c9cb22528f41cb8963665c372caf6"}, - {file = "pydantic_core-2.18.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:666e45cf071669fde468886654742fa10b0e74cd0fa0430a46ba6056b24fb0af"}, - {file = "pydantic_core-2.18.3-cp39-none-win32.whl", hash = "sha256:f9c08cabff68704a1b4667d33f534d544b8a07b8e5d039c37067fceb18789e78"}, - {file = "pydantic_core-2.18.3-cp39-none-win_amd64.whl", hash = "sha256:4afa5f5973e8572b5c0dcb4e2d4fda7890e7cd63329bd5cc3263a25c92ef0026"}, - {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:77319771a026f7c7d29c6ebc623de889e9563b7087911b46fd06c044a12aa5e9"}, - {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:df11fa992e9f576473038510d66dd305bcd51d7dd508c163a8c8fe148454e059"}, - {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d531076bdfb65af593326ffd567e6ab3da145020dafb9187a1d131064a55f97c"}, - {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d33ce258e4e6e6038f2b9e8b8a631d17d017567db43483314993b3ca345dcbbb"}, - {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1f9cd7f5635b719939019be9bda47ecb56e165e51dd26c9a217a433e3d0d59a9"}, - {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cd4a032bb65cc132cae1fe3e52877daecc2097965cd3914e44fbd12b00dae7c5"}, - {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f2718430098bcdf60402136c845e4126a189959d103900ebabb6774a5d9fdb"}, - {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c0037a92cf0c580ed14e10953cdd26528e8796307bb8bb312dc65f71547df04d"}, - {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b95a0972fac2b1ff3c94629fc9081b16371dad870959f1408cc33b2f78ad347a"}, - {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a62e437d687cc148381bdd5f51e3e81f5b20a735c55f690c5be94e05da2b0d5c"}, - {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b367a73a414bbb08507da102dc2cde0fa7afe57d09b3240ce82a16d608a7679c"}, - {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ecce4b2360aa3f008da3327d652e74a0e743908eac306198b47e1c58b03dd2b"}, - {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd4435b8d83f0c9561a2a9585b1de78f1abb17cb0cef5f39bf6a4b47d19bafe3"}, - {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:616221a6d473c5b9aa83fa8982745441f6a4a62a66436be9445c65f241b86c94"}, - {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:7e6382ce89a92bc1d0c0c5edd51e931432202b9080dc921d8d003e616402efd1"}, - {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ff58f379345603d940e461eae474b6bbb6dab66ed9a851ecd3cb3709bf4dcf6a"}, - {file = "pydantic_core-2.18.3.tar.gz", hash = "sha256:432e999088d85c8f36b9a3f769a8e2b57aabd817bbb729a90d1fe7f18f6f1f39"}, + {file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"}, + {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"}, + {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"}, + {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"}, + {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"}, + {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"}, + {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"}, + {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"}, + {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"}, + {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"}, + {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"}, + {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"}, + {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"}, + {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"}, + {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"}, + {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"}, + {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"}, + {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"}, + {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"}, + {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"}, + {file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"}, + {file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"}, + {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"}, + {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"}, + {file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"}, + {file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"}, + {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"}, + {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"}, + {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"}, + {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"}, + {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"}, + {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"}, + {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"}, ] [package.dependencies] @@ -217,13 +217,13 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pytest" -version = "8.2.1" +version = "8.2.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, - {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, + {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, + {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, ] [package.dependencies] @@ -255,6 +255,20 @@ pytest = ">=7.0.0,<9" docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + [[package]] name = "ruff" version = "0.3.7" @@ -294,16 +308,16 @@ files = [ [[package]] name = "typing-extensions" -version = "4.12.0" +version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"}, - {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "6b3769ef7aa960561350e90987a4934bf0b183e2f82ed28a6597be953154fec5" +content-hash = "c0890bb7d42663b172b862caa5d317f325f92d8bd1e8e6a9e211461b189f22dc" diff --git a/integ-tests/python/pyproject.toml b/integ-tests/python/pyproject.toml index 4a4873cf1..e50d88083 100644 --- a/integ-tests/python/pyproject.toml +++ b/integ-tests/python/pyproject.toml @@ -18,6 +18,7 @@ maturin = "^1.5.1" pytest-asyncio = "^0.23.7" pytest = "^8.2.1" pydantic = "^2.7.1" +python-dotenv = "^1.0.1" [build-system] requires = ["poetry-core"] diff --git a/integ-tests/python/app/test_functions.py b/integ-tests/python/test_functions.py similarity index 90% rename from integ-tests/python/app/test_functions.py rename to integ-tests/python/test_functions.py index afb26a647..ad78bb6aa 100644 --- a/integ-tests/python/app/test_functions.py +++ b/integ-tests/python/test_functions.py @@ -1,5 +1,6 @@ import pytest - +from dotenv import load_dotenv +load_dotenv() import baml_py from baml_client import b from baml_client.types import NamedArgsSingleEnumList, NamedArgsSingleClass @@ -8,6 +9,7 @@ import datetime + @pytest.mark.asyncio async def test_should_work_for_all_inputs(): res = await b.TestFnNamedArgsSingleBool(True) @@ -121,6 +123,15 @@ async def test_claude(): assert len(res) > 0, "Expected non-empty result but got empty." +@pytest.mark.asyncio +async def test_gemini(): + geminiRes = await b.TestGemini(input= "Dr. Pepper") + print(f'LLM output from Gemini: {geminiRes}') + + assert len(geminiRes) > 0, "Expected non-empty result but got empty." + + + @pytest.mark.asyncio async def test_streaming(): stream = b.stream.PromptTestOpenAI(input="Programming languages are fun to create") @@ -173,6 +184,32 @@ async def test_streaming_claude(): assert msgs[-1] == final, "Expected last stream message to match final response." +@pytest.mark.asyncio +async def test_streaming_gemini(): + stream = b.stream.TestGemini(input="Dr.Pepper") + msgs = [] + async for msg in stream: + msgs.append(msg) + final = await stream.get_final_response() + + assert len(final) > 0, "Expected non-empty final but got empty." + assert len(msgs) > 0, "Expected at least one streamed response but got none." + for prev_msg, msg in zip(msgs, msgs[1:]): + assert msg.startswith( + prev_msg + ), "Expected messages to be continuous, but prev was %r and next was %r" % ( + prev_msg, + msg, + ) + print("msgs:") + print(msgs[-1]) + print("final:") + print(final) + assert msgs[-1] == final, "Expected last stream message to match final response." + + + + @pytest.mark.asyncio async def test_tracing_async(): # sync_dummy_func("second-dummycall-arg") diff --git a/integ-tests/ruby/baml_client/client.rb b/integ-tests/ruby/baml_client/client.rb index c128867f6..593d17a21 100644 --- a/integ-tests/ruby/baml_client/client.rb +++ b/integ-tests/ruby/baml_client/client.rb @@ -789,6 +789,46 @@ def PromptTestOpenAIChatNoSystem( (raw.parsed_using_types(Baml::Types)) end + sig { + + params( + input: String, + ).returns(String) + + } + def TestAnthropic( + input: + ) + raw = @runtime.call_function( + "TestAnthropic", + { + "input" => input, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + + params( + input: String, + ).returns(String) + + } + def TestAzure( + input: + ) + raw = @runtime.call_function( + "TestAzure", + { + "input" => input, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + sig { returns(String) @@ -967,6 +1007,26 @@ def TestFnNamedArgsSingleStringList( (raw.parsed_using_types(Baml::Types)) end + sig { + + params( + input: String, + ).returns(String) + + } + def TestGemini( + input: + ) + raw = @runtime.call_function( + "TestGemini", + { + "input" => input, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + sig { params( @@ -1027,6 +1087,26 @@ def TestOllama( (raw.parsed_using_types(Baml::Types)) end + sig { + + params( + input: String, + ).returns(String) + + } + def TestOpenAI( + input: + ) + raw = @runtime.call_function( + "TestOpenAI", + { + "input" => input, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + sig { returns(String) @@ -1873,6 +1953,48 @@ def PromptTestOpenAIChatNoSystem( ) end + sig { + params( + input: String, + ).returns(Baml::BamlStream[String]) + } + def TestAnthropic( + input: + ) + raw = @runtime.stream_function( + "TestAnthropic", + { + "input" => input, + }, + @ctx_manager, + ) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + input: String, + ).returns(Baml::BamlStream[String]) + } + def TestAzure( + input: + ) + raw = @runtime.stream_function( + "TestAzure", + { + "input" => input, + }, + @ctx_manager, + ) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + sig { params( @@ -2062,6 +2184,27 @@ def TestFnNamedArgsSingleStringList( ) end + sig { + params( + input: String, + ).returns(Baml::BamlStream[String]) + } + def TestGemini( + input: + ) + raw = @runtime.stream_function( + "TestGemini", + { + "input" => input, + }, + @ctx_manager, + ) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + sig { params( img: Baml::Image, @@ -2125,6 +2268,27 @@ def TestOllama( ) end + sig { + params( + input: String, + ).returns(Baml::BamlStream[String]) + } + def TestOpenAI( + input: + ) + raw = @runtime.stream_function( + "TestOpenAI", + { + "input" => input, + }, + @ctx_manager, + ) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + sig { params( diff --git a/integ-tests/ruby/baml_client/inlined.rb b/integ-tests/ruby/baml_client/inlined.rb index 5f7f117ed..50b11f420 100644 --- a/integ-tests/ruby/baml_client/inlined.rb +++ b/integ-tests/ruby/baml_client/inlined.rb @@ -16,7 +16,7 @@ module Baml module Inlined FILE_MAP = { - "clients.baml" => "retry_policy Bar {\n max_retries 3\n strategy {\n type exponential_backoff\n }\n}\n\nretry_policy Foo {\n max_retries 3\n strategy {\n type constant_delay\n delay_ms 100\n }\n}\n\nclient GPT4 {\n provider baml-openai-chat\n options {\n model gpt-4\n api_key env.OPENAI_API_KEY\n }\n} \n\n\nclient GPT4o {\n provider baml-openai-chat\n options {\n model gpt-4o\n api_key env.OPENAI_API_KEY\n }\n} \n\n\nclient GPT4Turbo {\n retry_policy Bar\n provider baml-openai-chat\n options {\n model gpt-4-turbo\n api_key env.OPENAI_API_KEY\n }\n} \n\nclient GPT35 {\n provider baml-openai-chat\n options {\n model \"gpt-3.5-turbo\"\n api_key env.OPENAI_API_KEY\n }\n}\n\nclient Ollama {\n provider ollama\n options {\n model llama2\n api_key \"\"\n }\n}\n\nclient GPT35Azure {\n provider azure-openai\n options {\n resource_name \"west-us-azure-baml\"\n deployment_id \"gpt-35-turbo-default\"\n // base_url \"https://west-us-azure-baml.openai.azure.com/openai/deployments/gpt-35-turbo-default\"\n api_version \"2024-02-01\"\n api_key env.AZURE_OPENAI_API_KEY\n }\n}\n\n\nclient Claude {\n provider anthropic\n options {\n model claude-3-haiku-20240307\n api_key env.ANTHROPIC_API_KEY\n max_tokens 1000\n }\n}\n\nclient Resilient_SimpleSyntax {\n retry_policy Foo\n provider baml-fallback\n options {\n strategy [\n GPT4Turbo\n GPT35\n Lottery_SimpleSyntax\n ]\n }\n} \n \nclient Lottery_SimpleSyntax {\n provider baml-round-robin\n options {\n start 0\n strategy [\n GPT35\n Claude\n ]\n }\n}\n", + "clients.baml" => "retry_policy Bar {\n max_retries 3\n strategy {\n type exponential_backoff\n }\n}\n\nretry_policy Foo {\n max_retries 3\n strategy {\n type constant_delay\n delay_ms 100\n }\n}\n\nclient GPT4 {\n provider baml-openai-chat\n options {\n model gpt-4\n api_key env.OPENAI_API_KEY\n }\n} \n\n\nclient GPT4o {\n provider baml-openai-chat\n options {\n model gpt-4o\n api_key env.OPENAI_API_KEY\n }\n} \n\n\nclient GPT4Turbo {\n retry_policy Bar\n provider baml-openai-chat\n options {\n model gpt-4-turbo\n api_key env.OPENAI_API_KEY\n }\n} \n\nclient GPT35 {\n provider baml-openai-chat\n options {\n model \"gpt-3.5-turbo\"\n api_key env.OPENAI_API_KEY\n }\n}\n\nclient Ollama {\n provider ollama\n options {\n model llama2\n api_key \"\"\n }\n}\n\nclient GPT35Azure {\n provider azure-openai\n options {\n resource_name \"west-us-azure-baml\"\n deployment_id \"gpt-35-turbo-default\"\n // base_url \"https://west-us-azure-baml.openai.azure.com/openai/deployments/gpt-35-turbo-default\"\n api_version \"2024-02-01\"\n api_key env.AZURE_OPENAI_API_KEY\n }\n}\n\nclient Gemini {\n provider baml-google-chat\n options{\n model \"gemini-1.5-pro-001\"\n api_key env.GOOGLE_API_KEY\n project_id env.GOOGLE_PROJECT_ID\n }\n}\n\n\nclient Claude {\n provider anthropic\n options {\n model claude-3-haiku-20240307\n api_key env.ANTHROPIC_API_KEY\n max_tokens 1000\n }\n}\n\nclient Resilient_SimpleSyntax {\n retry_policy Foo\n provider baml-fallback\n options {\n strategy [\n GPT4Turbo\n GPT35\n Lottery_SimpleSyntax\n ]\n }\n} \n \nclient Lottery_SimpleSyntax {\n provider baml-round-robin\n options {\n start 0\n strategy [\n GPT35\n Claude\n ]\n }\n}\n", "fiddle-examples/chain-of-thought.baml" => "class Email {\n subject string\n body string\n from_address string\n}\n\nenum OrderStatus {\n ORDERED\n SHIPPED\n DELIVERED\n CANCELLED\n}\n\nclass OrderInfo {\n order_status OrderStatus\n tracking_number string?\n estimated_arrival_date string?\n}\n\nfunction GetOrderInfo(email: Email) -> OrderInfo {\n client GPT4\n prompt #\"\n Given the email below:\n\n ```\n from: {{email.from_address}}\n Email Subject: {{email.subject}}\n Email Body: {{email.body}}\n ```\n\n Extract this info from the email in JSON format:\n {{ ctx.output_format }}\n\n Before you output the JSON, please explain your\n reasoning step-by-step. Here is an example on how to do this:\n 'If we think step by step we can see that ...\n therefore the output JSON is:\n {\n ... the json schema ...\n }'\n \"#\n}", "fiddle-examples/chat-roles.baml" => "// This will be available as an enum in your Python and Typescript code.\nenum Category2 {\n Refund\n CancelOrder\n TechnicalSupport\n AccountIssue\n Question\n}\n\nfunction ClassifyMessage2(input: string) -> Category {\n client GPT4\n\n prompt #\"\n {{ _.role(\"system\") }}\n // You can use _.role(\"system\") to indicate that this text should be a system message\n\n Classify the following INPUT into ONE\n of the following categories:\n\n {{ ctx.output_format }}\n\n {{ _.role(\"user\") }}\n // And _.role(\"user\") to indicate that this text should be a user message\n\n INPUT: {{ input }}\n\n Response:\n \"#\n}", "fiddle-examples/classify-message.baml" => "// This will be available as an enum in your Python and Typescript code.\nenum Category {\n Refund\n CancelOrder\n TechnicalSupport\n AccountIssue\n Question\n}\n\nfunction ClassifyMessage(input: string) -> Category {\n client GPT4\n\n prompt #\"\n Classify the following INPUT into ONE\n of the following categories:\n\n INPUT: {{ input }}\n\n {{ ctx.output_format }}\n\n Response:\n \"#\n}", @@ -55,7 +55,7 @@ module Inlined "test-files/functions/prompts/no-chat-messages.baml" => "\n\nfunction PromptTestClaude(input: string) -> string {\n client Claude\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAI(input: string) -> string {\n client GPT35\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}", "test-files/functions/prompts/with-chat-messages.baml" => "\nfunction PromptTestOpenAIChat(input: string) -> string {\n client GPT35\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAIChatNoSystem(input: string) -> string {\n client GPT35\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChat(input: string) -> string {\n client Claude\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChatNoSystem(input: string) -> string {\n client Claude\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\ntest PromptTestOpenAIChat {\n functions [PromptTestClaude, PromptTestOpenAI, PromptTestOpenAIChat, PromptTestOpenAIChatNoSystem, PromptTestClaudeChat, PromptTestClaudeChatNoSystem]\n args {\n input \"cats\"\n }\n}\n\ntest TestClaude {\n functions [PromptTestClaudeChatNoSystem]\n args {\n input \"lion\"\n }\n}", "test-files/functions/v2/basic.baml" => "\n\nfunction ExtractResume2(resume: string) -> Resume {\n client GPT4\n prompt #\"\n {{ _.role('system') }}\n\n Extract the following information from the resume:\n\n Resume:\n <<<<\n {{ resume }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}\n\n\nclass WithReasoning {\n value string\n reasoning string @description(#\"\n Why the value is a good fit.\n \"#)\n}\n\n\nclass SearchParams {\n dateRange int? @description(#\"\n In ISO duration format, e.g. P1Y2M10D.\n \"#)\n location string[]\n jobTitle WithReasoning? @description(#\"\n An exact job title, not a general category.\n \"#)\n company WithReasoning? @description(#\"\n The exact name of the company, not a product or service.\n \"#)\n description WithReasoning[] @description(#\"\n Any specific projects or features the user is looking for.\n \"#)\n tags (Tag | string)[]\n}\n\nenum Tag {\n Security\n AI\n Blockchain\n}\n\nfunction GetQuery(query: string) -> SearchParams {\n client GPT4\n prompt #\"\n Extract the following information from the query:\n\n Query:\n <<<<\n {{ query }}\n <<<<\n\n OUTPUT_JSON_SCHEMA:\n {{ ctx.output_format }}\n\n Before OUTPUT_JSON_SCHEMA, list 5 intentions the user may have.\n --- EXAMPLES ---\n 1. \n 2. \n 3. \n 4. \n 5. \n\n {\n ... // OUTPUT_JSON_SCHEMA\n }\n \"#\n}\n\nclass RaysData {\n dataType DataType\n value Resume | Event\n}\n\nenum DataType {\n Resume\n Event\n}\n\nclass Event {\n title string\n date string\n location string\n description string\n}\n\nfunction GetDataType(text: string) -> RaysData {\n client GPT4\n prompt #\"\n Extract the relevant info.\n\n Text:\n <<<<\n {{ text }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}", - "test-files/providers/providers.baml" => "\n\nfunction TestOllama(input: string) -> string {\n client Ollama\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\ntest TestProvider {\n functions [TestOllama]\n args {\n input \"the moon\"\n }\n}\n", + "test-files/providers/providers.baml" => "function TestAnthropic(input: string) -> string {\n client Claude\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOpenAI(input: string) -> string {\n client GPT35\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestAzure(input: string) -> string {\n client GPT35Azure\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOllama(input: string) -> string {\n client Ollama\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestGemini(input: string) -> string {\n client Gemini\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\n\ntest TestProvider {\n functions [TestAnthropic, TestOpenAI, TestAzure, TestOllama, TestGemini]\n args {\n input \"the moon\"\n }\n}\n\n\n", "test-files/strategies/fallback.baml" => "\nclient FaultyClient {\n provider openai\n options {\n model unknown-model\n api_key env.OPENAI_API_KEY\n }\n}\n\n\nclient FallbackClient {\n provider fallback\n options {\n // first 2 clients are expected to fail.\n strategy [\n FaultyClient,\n RetryClientConstant,\n GPT35\n ]\n }\n}\n\nfunction TestFallbackClient() -> string {\n client FallbackClient\n // TODO make it return the client name instead\n prompt #\"\n Say a haiku about mexico.\n \"#\n}", "test-files/strategies/retry.baml" => "\nretry_policy Exponential {\n max_retries 3\n strategy {\n type exponential_backoff\n }\n}\n\nretry_policy Constant {\n max_retries 3\n strategy {\n type constant_delay\n delay_ms 100\n }\n}\n\nclient RetryClientConstant {\n provider openai\n retry_policy Constant\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blah\"\n }\n}\n\nclient RetryClientExponential {\n provider openai\n retry_policy Exponential\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blahh\"\n }\n}\n\nfunction TestRetryConstant() -> string {\n client RetryClientConstant\n prompt #\"\n Say a haiku\n \"#\n}\n\nfunction TestRetryExponential() -> string {\n client RetryClientExponential\n prompt #\"\n Say a haiku\n \"#\n}\n", "test-files/strategies/roundrobin.baml" => "", diff --git a/integ-tests/typescript/baml_client/client.ts b/integ-tests/typescript/baml_client/client.ts index f65f0db37..1c80bb183 100644 --- a/integ-tests/typescript/baml_client/client.ts +++ b/integ-tests/typescript/baml_client/client.ts @@ -586,6 +586,36 @@ export class BamlClient { return raw.parsed() as string } + async TestAnthropic( + input: string, + __baml_options__?: { tb?: TypeBuilder } + ): Promise { + const raw = await this.runtime.callFunction( + "TestAnthropic", + { + "input": input + }, + this.ctx_manager.get(), + __baml_options__?.tb?.__tb(), + ) + return raw.parsed() as string + } + + async TestAzure( + input: string, + __baml_options__?: { tb?: TypeBuilder } + ): Promise { + const raw = await this.runtime.callFunction( + "TestAzure", + { + "input": input + }, + this.ctx_manager.get(), + __baml_options__?.tb?.__tb(), + ) + return raw.parsed() as string + } + async TestFallbackClient( __baml_options__?: { tb?: TypeBuilder } @@ -721,6 +751,21 @@ export class BamlClient { return raw.parsed() as string } + async TestGemini( + input: string, + __baml_options__?: { tb?: TypeBuilder } + ): Promise { + const raw = await this.runtime.callFunction( + "TestGemini", + { + "input": input + }, + this.ctx_manager.get(), + __baml_options__?.tb?.__tb(), + ) + return raw.parsed() as string + } + async TestImageInput( img: Image, __baml_options__?: { tb?: TypeBuilder } @@ -766,6 +811,21 @@ export class BamlClient { return raw.parsed() as string } + async TestOpenAI( + input: string, + __baml_options__?: { tb?: TypeBuilder } + ): Promise { + const raw = await this.runtime.callFunction( + "TestOpenAI", + { + "input": input + }, + this.ctx_manager.get(), + __baml_options__?.tb?.__tb(), + ) + return raw.parsed() as string + } + async TestRetryConstant( __baml_options__?: { tb?: TypeBuilder } @@ -1631,6 +1691,50 @@ class BamlStreamClient { ) } + TestAnthropic( + input: string, + __baml_options__?: { tb?: TypeBuilder } + ): BamlStream<(string | null), string> { + const raw = this.runtime.streamFunction( + "TestAnthropic", + { + "input": input + }, + undefined, + this.ctx_manager.get(), + __baml_options__?.tb?.__tb(), + ) + return new BamlStream<(string | null), string>( + raw, + (a): a is (string | null) => a, + (a): a is string => a, + this.ctx_manager.get(), + __baml_options__?.tb?.__tb(), + ) + } + + TestAzure( + input: string, + __baml_options__?: { tb?: TypeBuilder } + ): BamlStream<(string | null), string> { + const raw = this.runtime.streamFunction( + "TestAzure", + { + "input": input + }, + undefined, + this.ctx_manager.get(), + __baml_options__?.tb?.__tb(), + ) + return new BamlStream<(string | null), string>( + raw, + (a): a is (string | null) => a, + (a): a is string => a, + this.ctx_manager.get(), + __baml_options__?.tb?.__tb(), + ) + } + TestFallbackClient( __baml_options__?: { tb?: TypeBuilder } @@ -1829,6 +1933,28 @@ class BamlStreamClient { ) } + TestGemini( + input: string, + __baml_options__?: { tb?: TypeBuilder } + ): BamlStream<(string | null), string> { + const raw = this.runtime.streamFunction( + "TestGemini", + { + "input": input + }, + undefined, + this.ctx_manager.get(), + __baml_options__?.tb?.__tb(), + ) + return new BamlStream<(string | null), string>( + raw, + (a): a is (string | null) => a, + (a): a is string => a, + this.ctx_manager.get(), + __baml_options__?.tb?.__tb(), + ) + } + TestImageInput( img: Image, __baml_options__?: { tb?: TypeBuilder } @@ -1895,6 +2021,28 @@ class BamlStreamClient { ) } + TestOpenAI( + input: string, + __baml_options__?: { tb?: TypeBuilder } + ): BamlStream<(string | null), string> { + const raw = this.runtime.streamFunction( + "TestOpenAI", + { + "input": input + }, + undefined, + this.ctx_manager.get(), + __baml_options__?.tb?.__tb(), + ) + return new BamlStream<(string | null), string>( + raw, + (a): a is (string | null) => a, + (a): a is string => a, + this.ctx_manager.get(), + __baml_options__?.tb?.__tb(), + ) + } + TestRetryConstant( __baml_options__?: { tb?: TypeBuilder } diff --git a/integ-tests/typescript/baml_client/inlinedbaml.ts b/integ-tests/typescript/baml_client/inlinedbaml.ts index f25774f04..80b7a2717 100644 --- a/integ-tests/typescript/baml_client/inlinedbaml.ts +++ b/integ-tests/typescript/baml_client/inlinedbaml.ts @@ -17,14 +17,14 @@ $ pnpm add @boundaryml/baml /* eslint-disable */ const fileMap = { - "clients.baml": "retry_policy Bar {\n max_retries 3\n strategy {\n type exponential_backoff\n }\n}\n\nretry_policy Foo {\n max_retries 3\n strategy {\n type constant_delay\n delay_ms 100\n }\n}\n\nclient GPT4 {\n provider baml-openai-chat\n options {\n model gpt-4\n api_key env.OPENAI_API_KEY\n }\n} \n\n\nclient GPT4o {\n provider baml-openai-chat\n options {\n model gpt-4o\n api_key env.OPENAI_API_KEY\n }\n} \n\n\nclient GPT4Turbo {\n retry_policy Bar\n provider baml-openai-chat\n options {\n model gpt-4-turbo\n api_key env.OPENAI_API_KEY\n }\n} \n\nclient GPT35 {\n provider baml-openai-chat\n options {\n model \"gpt-3.5-turbo\"\n api_key env.OPENAI_API_KEY\n }\n}\n\nclient Ollama {\n provider ollama\n options {\n model llama2\n api_key \"\"\n }\n}\n\nclient GPT35Azure {\n provider azure-openai\n options {\n resource_name \"west-us-azure-baml\"\n deployment_id \"gpt-35-turbo-default\"\n // base_url \"https://west-us-azure-baml.openai.azure.com/openai/deployments/gpt-35-turbo-default\"\n api_version \"2024-02-01\"\n api_key env.AZURE_OPENAI_API_KEY\n }\n}\n\n\nclient Claude {\n provider anthropic\n options {\n model claude-3-haiku-20240307\n api_key env.ANTHROPIC_API_KEY\n max_tokens 1000\n }\n}\n\nclient Resilient_SimpleSyntax {\n retry_policy Foo\n provider baml-fallback\n options {\n strategy [\n GPT4Turbo\n GPT35\n Lottery_SimpleSyntax\n ]\n }\n} \n \nclient Lottery_SimpleSyntax {\n provider baml-round-robin\n options {\n start 0\n strategy [\n GPT35\n Claude\n ]\n }\n}\n", + "clients.baml": "retry_policy Bar {\n max_retries 3\n strategy {\n type exponential_backoff\n }\n}\n\nretry_policy Foo {\n max_retries 3\n strategy {\n type constant_delay\n delay_ms 100\n }\n}\n\nclient GPT4 {\n provider baml-openai-chat\n options {\n model gpt-4\n api_key env.OPENAI_API_KEY\n }\n} \n\n\nclient GPT4o {\n provider baml-openai-chat\n options {\n model gpt-4o\n api_key env.OPENAI_API_KEY\n }\n} \n\n\nclient GPT4Turbo {\n retry_policy Bar\n provider baml-openai-chat\n options {\n model gpt-4-turbo\n api_key env.OPENAI_API_KEY\n }\n} \n\nclient GPT35 {\n provider baml-openai-chat\n options {\n model \"gpt-3.5-turbo\"\n api_key env.OPENAI_API_KEY\n }\n}\n\nclient Ollama {\n provider ollama\n options {\n model llama2\n api_key \"\"\n }\n}\n\nclient GPT35Azure {\n provider azure-openai\n options {\n resource_name \"west-us-azure-baml\"\n deployment_id \"gpt-35-turbo-default\"\n // base_url \"https://west-us-azure-baml.openai.azure.com/openai/deployments/gpt-35-turbo-default\"\n api_version \"2024-02-01\"\n api_key env.AZURE_OPENAI_API_KEY\n }\n}\n\nclient Gemini {\n provider google-ai\n options{\n model \"gemini-1.5-pro-001\"\n api_key env.GOOGLE_API_KEY\n }\n}\n\n\nclient Claude {\n provider anthropic\n options {\n model claude-3-haiku-20240307\n api_key env.ANTHROPIC_API_KEY\n max_tokens 1000\n }\n}\n\nclient Resilient_SimpleSyntax {\n retry_policy Foo\n provider baml-fallback\n options {\n strategy [\n GPT4Turbo\n GPT35\n Lottery_SimpleSyntax\n ]\n }\n} \n \nclient Lottery_SimpleSyntax {\n provider baml-round-robin\n options {\n start 0\n strategy [\n GPT35\n Claude\n ]\n }\n}\n", "fiddle-examples/chain-of-thought.baml": "class Email {\n subject string\n body string\n from_address string\n}\n\nenum OrderStatus {\n ORDERED\n SHIPPED\n DELIVERED\n CANCELLED\n}\n\nclass OrderInfo {\n order_status OrderStatus\n tracking_number string?\n estimated_arrival_date string?\n}\n\nfunction GetOrderInfo(email: Email) -> OrderInfo {\n client GPT4\n prompt #\"\n Given the email below:\n\n ```\n from: {{email.from_address}}\n Email Subject: {{email.subject}}\n Email Body: {{email.body}}\n ```\n\n Extract this info from the email in JSON format:\n {{ ctx.output_format }}\n\n Before you output the JSON, please explain your\n reasoning step-by-step. Here is an example on how to do this:\n 'If we think step by step we can see that ...\n therefore the output JSON is:\n {\n ... the json schema ...\n }'\n \"#\n}", "fiddle-examples/chat-roles.baml": "// This will be available as an enum in your Python and Typescript code.\nenum Category2 {\n Refund\n CancelOrder\n TechnicalSupport\n AccountIssue\n Question\n}\n\nfunction ClassifyMessage2(input: string) -> Category {\n client GPT4\n\n prompt #\"\n {{ _.role(\"system\") }}\n // You can use _.role(\"system\") to indicate that this text should be a system message\n\n Classify the following INPUT into ONE\n of the following categories:\n\n {{ ctx.output_format }}\n\n {{ _.role(\"user\") }}\n // And _.role(\"user\") to indicate that this text should be a user message\n\n INPUT: {{ input }}\n\n Response:\n \"#\n}", "fiddle-examples/classify-message.baml": "// This will be available as an enum in your Python and Typescript code.\nenum Category {\n Refund\n CancelOrder\n TechnicalSupport\n AccountIssue\n Question\n}\n\nfunction ClassifyMessage(input: string) -> Category {\n client GPT4\n\n prompt #\"\n Classify the following INPUT into ONE\n of the following categories:\n\n INPUT: {{ input }}\n\n {{ ctx.output_format }}\n\n Response:\n \"#\n}", "fiddle-examples/extract-names.baml": "function ExtractNames(input: string) -> string[] {\n client GPT4\n prompt #\"\n Extract the names from this INPUT:\n \n INPUT:\n ---\n {{ input }}\n ---\n\n {{ ctx.output_format }}\n\n Response:\n \"#\n}\n", "fiddle-examples/images/image.baml": "function DescribeImage(img: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"user\") }}\n\n\n Describe the image below in 5 words:\n {{ img }}\n \"#\n\n}\n\nclass FakeImage {\n url string\n}\n\nclass ClassWithImage {\n myImage image\n param2 string\n fake_image FakeImage\n}\n\n// chat role user present\nfunction DescribeImage2(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"user\") }}\n You should return 2 answers that answer the following commands.\n\n 1. Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n 2. Also tell me what's happening here in one sentence:\n {{ img2 }}\n \"#\n}\n\n// no chat role\nfunction DescribeImage3(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n Tell me also what's happening here in one sentence and relate it to the word {{ classWithImage.param2 }}:\n {{ img2 }}\n \"#\n}\n\n\n// system prompt and chat prompt\nfunction DescribeImage4(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"system\")}}\n\n Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n Tell me also what's happening here in one sentence and relate it to the word {{ classWithImage.param2 }}:\n {{ img2 }}\n \"#\n}", "fiddle-examples/symbol-tuning.baml": "enum Category3 {\n Refund @alias(\"k1\")\n @description(\"Customer wants to refund a product\")\n\n CancelOrder @alias(\"k2\")\n @description(\"Customer wants to cancel an order\")\n\n TechnicalSupport @alias(\"k3\")\n @description(\"Customer needs help with a technical issue unrelated to account creation or login\")\n\n AccountIssue @alias(\"k4\")\n @description(\"Specifically relates to account-login or account-creation\")\n\n Question @alias(\"k5\")\n @description(\"Customer has a question\")\n}\n\nfunction ClassifyMessage3(input: string) -> Category {\n client GPT4\n\n prompt #\"\n Classify the following INPUT into ONE\n of the following categories:\n\n INPUT: {{ input }}\n\n {{ ctx.output_format }}\n\n Response:\n \"#\n}", - "main.baml": "generator lang_python {\n output_type python/pydantic\n output_dir \"../python\"\n}\n\ngenerator lang_typescript {\n output_type typescript\n output_dir \"../typescript\"\n}\n\ngenerator lang_ruby {\n output_type ruby/sorbet\n output_dir \"../ruby\"\n}\n", + "main.baml": "generator lang_python {\n output_type python/pydantic\n output_dir \"../python\"\n}\n\ngenerator lang_typescript {\n output_type typescript\n output_dir \"../typescript\"\n}\n\n// generator lang_ruby {\n// output_type ruby/sorbet\n// output_dir \"../ruby\"\n// }\n", "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(\n User is confused\n )\n E @description(\n User is excited\n )\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", @@ -56,7 +56,7 @@ const fileMap = { "test-files/functions/prompts/no-chat-messages.baml": "\n\nfunction PromptTestClaude(input: string) -> string {\n client Claude\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAI(input: string) -> string {\n client GPT35\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}", "test-files/functions/prompts/with-chat-messages.baml": "\nfunction PromptTestOpenAIChat(input: string) -> string {\n client GPT35\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAIChatNoSystem(input: string) -> string {\n client GPT35\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChat(input: string) -> string {\n client Claude\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChatNoSystem(input: string) -> string {\n client Claude\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\ntest PromptTestOpenAIChat {\n functions [PromptTestClaude, PromptTestOpenAI, PromptTestOpenAIChat, PromptTestOpenAIChatNoSystem, PromptTestClaudeChat, PromptTestClaudeChatNoSystem]\n args {\n input \"cats\"\n }\n}\n\ntest TestClaude {\n functions [PromptTestClaudeChatNoSystem]\n args {\n input \"lion\"\n }\n}", "test-files/functions/v2/basic.baml": "\n\nfunction ExtractResume2(resume: string) -> Resume {\n client GPT4\n prompt #\"\n {{ _.role('system') }}\n\n Extract the following information from the resume:\n\n Resume:\n <<<<\n {{ resume }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}\n\n\nclass WithReasoning {\n value string\n reasoning string @description(#\"\n Why the value is a good fit.\n \"#)\n}\n\n\nclass SearchParams {\n dateRange int? @description(#\"\n In ISO duration format, e.g. P1Y2M10D.\n \"#)\n location string[]\n jobTitle WithReasoning? @description(#\"\n An exact job title, not a general category.\n \"#)\n company WithReasoning? @description(#\"\n The exact name of the company, not a product or service.\n \"#)\n description WithReasoning[] @description(#\"\n Any specific projects or features the user is looking for.\n \"#)\n tags (Tag | string)[]\n}\n\nenum Tag {\n Security\n AI\n Blockchain\n}\n\nfunction GetQuery(query: string) -> SearchParams {\n client GPT4\n prompt #\"\n Extract the following information from the query:\n\n Query:\n <<<<\n {{ query }}\n <<<<\n\n OUTPUT_JSON_SCHEMA:\n {{ ctx.output_format }}\n\n Before OUTPUT_JSON_SCHEMA, list 5 intentions the user may have.\n --- EXAMPLES ---\n 1. \n 2. \n 3. \n 4. \n 5. \n\n {\n ... // OUTPUT_JSON_SCHEMA\n }\n \"#\n}\n\nclass RaysData {\n dataType DataType\n value Resume | Event\n}\n\nenum DataType {\n Resume\n Event\n}\n\nclass Event {\n title string\n date string\n location string\n description string\n}\n\nfunction GetDataType(text: string) -> RaysData {\n client GPT4\n prompt #\"\n Extract the relevant info.\n\n Text:\n <<<<\n {{ text }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}", - "test-files/providers/providers.baml": "\n\nfunction TestOllama(input: string) -> string {\n client Ollama\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\ntest TestProvider {\n functions [TestOllama]\n args {\n input \"the moon\"\n }\n}\n", + "test-files/providers/providers.baml": "function TestAnthropic(input: string) -> string {\n client Claude\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOpenAI(input: string) -> string {\n client GPT35\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestAzure(input: string) -> string {\n client GPT35Azure\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestOllama(input: string) -> string {\n client Ollama\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\nfunction TestGemini(input: string) -> string {\n client Gemini\n prompt #\"\n Write a nice short story about {{ input }}\n \"#\n}\n\n\ntest TestProvider {\n functions [TestAnthropic, TestOpenAI, TestAzure, TestOllama, TestGemini]\n args {\n input \"Donkey kong and peanut butter\"\n }\n}\n\n\n", "test-files/strategies/fallback.baml": "\nclient FaultyClient {\n provider openai\n options {\n model unknown-model\n api_key env.OPENAI_API_KEY\n }\n}\n\n\nclient FallbackClient {\n provider fallback\n options {\n // first 2 clients are expected to fail.\n strategy [\n FaultyClient,\n RetryClientConstant,\n GPT35\n ]\n }\n}\n\nfunction TestFallbackClient() -> string {\n client FallbackClient\n // TODO make it return the client name instead\n prompt #\"\n Say a haiku about mexico.\n \"#\n}", "test-files/strategies/retry.baml": "\nretry_policy Exponential {\n max_retries 3\n strategy {\n type exponential_backoff\n }\n}\n\nretry_policy Constant {\n max_retries 3\n strategy {\n type constant_delay\n delay_ms 100\n }\n}\n\nclient RetryClientConstant {\n provider openai\n retry_policy Constant\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blah\"\n }\n}\n\nclient RetryClientExponential {\n provider openai\n retry_policy Exponential\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blahh\"\n }\n}\n\nfunction TestRetryConstant() -> string {\n client RetryClientConstant\n prompt #\"\n Say a haiku\n \"#\n}\n\nfunction TestRetryExponential() -> string {\n client RetryClientExponential\n prompt #\"\n Say a haiku\n \"#\n}\n", "test-files/strategies/roundrobin.baml": "", diff --git a/integ-tests/typescript/tests/integ-tests.test.ts b/integ-tests/typescript/tests/integ-tests.test.ts index 5753d173f..f0cd66868 100644 --- a/integ-tests/typescript/tests/integ-tests.test.ts +++ b/integ-tests/typescript/tests/integ-tests.test.ts @@ -120,6 +120,22 @@ describe('Integ tests', () => { expect(msgs.at(-1)).toEqual(final) }) + it('should support streaming in Gemini', async () => { + const stream = b.stream.TestGemini('Dr. Pepper') + const msgs: string[] = [] + for await (const msg of stream) { + msgs.push(msg ?? '') + } + const final = await stream.getFinalResponse() + + expect(final.length).toBeGreaterThan(0) + expect(msgs.length).toBeGreaterThan(0) + for (let i = 0; i < msgs.length - 2; i++) { + expect(msgs[i + 1].startsWith(msgs[i])).toBeTruthy() + } + expect(msgs.at(-1)).toEqual(final) + }) + it('should support streaming without iterating', async () => { const final = await b.stream.PromptTestOpenAI('Mt Rainier is tall').getFinalResponse() expect(final.length).toBeGreaterThan(0) @@ -141,6 +157,8 @@ describe('Integ tests', () => { expect(msgs.at(-1)).toEqual(final) }) + + it('supports tracing sync', async () => { const blah = 'blah' diff --git a/typescript/vscode-ext/packages/vscode/src/extension.ts b/typescript/vscode-ext/packages/vscode/src/extension.ts index aa8037d69..e610ee9b2 100644 --- a/typescript/vscode-ext/packages/vscode/src/extension.ts +++ b/typescript/vscode-ext/packages/vscode/src/extension.ts @@ -269,13 +269,24 @@ export function activate(context: vscode.ExtensionContext) { app.use( createProxyMiddleware({ changeOrigin: true, + pathRewrite: (path, req) => { + // Ensure the URL does not end with a slash + if (path.endsWith('/')) { + return path.slice(0, -1) + } + return path + }, router: (req) => { // Extract the original target URL from the custom header - const originalUrl = req.headers['baml-original-url'] - + let originalUrl = req.headers['baml-original-url'] if (typeof originalUrl === 'string') { delete req.headers['baml-original-url'] req.headers['origin'] = `http://localhost:${port}` + + // Ensure the URL does not end with a slash + if (originalUrl.endsWith('/')) { + originalUrl = originalUrl.slice(0, -1) + } return originalUrl } else { throw new Error('baml-original-url header is missing or invalid') @@ -292,13 +303,12 @@ export function activate(context: vscode.ExtensionContext) { }, }), ) - } catch (error) { + } + catch (error) { console.error('Failed to start proxy server:', error) vscode.window.showErrorMessage('Failed to BAML localhost server. Contact support for help.') } - // Add a catch-all route to handle 404 errors - // TODO: Reactivate linter. // runDiagnostics(); } @@ -340,8 +350,6 @@ class DiagnosticCodeActionProvider implements vscode.CodeActionProvider { codeActions.push(fixAction) } } - - console.debug('Code actions:', codeActions) return codeActions } }