diff --git a/engine/baml-lib/baml-core/src/ir/walker.rs b/engine/baml-lib/baml-core/src/ir/walker.rs index 7e7b11c67..1a7d1a24b 100644 --- a/engine/baml-lib/baml-core/src/ir/walker.rs +++ b/engine/baml-lib/baml-core/src/ir/walker.rs @@ -15,32 +15,6 @@ use super::{ }; use crate::ir::jinja_helpers::render_expression; -fn provider_to_env_vars( - provider: &str, -) -> impl IntoIterator, &'static str)> { - match provider { - "aws-bedrock" => vec![ - (None, "AWS_ACCESS_KEY_ID"), - (None, "AWS_SECRET_ACCESS_KEY"), - (Some("region"), "AWS_REGION"), - ], - "openai" => vec![(Some("api_key"), "OPENAI_API_KEY")], - "anthropic" => vec![(Some("api_key"), "ANTHROPIC_API_KEY")], - "google-ai" => vec![(Some("api_key"), "GOOGLE_API_KEY")], - "vertex-ai" => vec![ - (Some("credentials"), "GOOGLE_APPLICATION_CREDENTIALS"), - ( - Some("credentials_content"), - "GOOGLE_APPLICATION_CREDENTIALS_CONTENT", - ), - ], - "azure-openai" => vec![(Some("api_key"), "AZURE_OPENAI_API_KEY")], - "openai-generic" => vec![(Some("api_key"), "OPENAI_API_KEY")], - "ollama" => vec![], - other => vec![], - } -} - impl<'a> Walker<'a, &'a FunctionNode> { pub fn name(&self) -> &'a str { self.elem().name() diff --git a/engine/baml-lib/llm-client/src/clients/openai.rs b/engine/baml-lib/llm-client/src/clients/openai.rs index 80c01996a..934e60e1f 100644 --- a/engine/baml-lib/llm-client/src/clients/openai.rs +++ b/engine/baml-lib/llm-client/src/clients/openai.rs @@ -1,6 +1,9 @@ use std::collections::HashSet; -use crate::{AllowedRoleMetadata, FinishReasonFilter, RolesSelection, SupportedRequestModes, UnresolvedAllowedRoleMetadata, UnresolvedFinishReasonFilter, UnresolvedRolesSelection}; +use crate::{ + AllowedRoleMetadata, FinishReasonFilter, RolesSelection, SupportedRequestModes, + UnresolvedAllowedRoleMetadata, UnresolvedFinishReasonFilter, UnresolvedRolesSelection, +}; use anyhow::Result; use baml_types::{GetEnvVar, StringOr, UnresolvedValue}; @@ -64,7 +67,12 @@ pub struct ResolvedOpenAI { impl ResolvedOpenAI { fn is_o1_model(&self) -> bool { - self.properties.get("model").is_some_and(|model| model.as_str().map(|s| s.starts_with("o1-")).unwrap_or(false)) + self.properties.get("model").is_some_and(|model| { + model + .as_str() + .map(|s| s.starts_with("o1-") || s.eq("o1")) + .unwrap_or(false) + }) } pub fn supports_streaming(&self) -> bool { @@ -79,7 +87,11 @@ impl ResolvedOpenAI { if self.is_o1_model() { vec!["user".to_string(), "assistant".to_string()] } else { - vec!["system".to_string(), "user".to_string(), "assistant".to_string()] + vec![ + "system".to_string(), + "user".to_string(), + "assistant".to_string(), + ] } }) } @@ -89,7 +101,6 @@ impl ResolvedOpenAI { // TODO: guard against empty allowed_roles // The compiler should already guarantee that this is non-empty self.allowed_roles().remove(0) - }) } } @@ -277,7 +288,10 @@ impl UnresolvedOpenAI { let mut instance = Self::create_common(properties, base_url, None)?; instance.query_params = query_params; - instance.headers.entry("api-key".to_string()).or_insert(api_key); + instance + .headers + .entry("api-key".to_string()) + .or_insert(api_key); Ok(instance) } @@ -300,7 +314,8 @@ impl UnresolvedOpenAI { let api_key = properties.ensure_api_key(); - let mut instance = Self::create_common(properties, Some(either::Either::Left(base_url)), api_key)?; + let mut instance = + Self::create_common(properties, Some(either::Either::Left(base_url)), api_key)?; // Ollama uses smaller models many of which prefer the user role if instance.role_selection.default.is_none() { instance.role_selection.default = Some(StringOr::Value("user".to_string())); diff --git a/fern/01-guide/04-baml-basics/error-handling.mdx b/fern/01-guide/04-baml-basics/error-handling.mdx index e3d37ab99..da4dc779a 100644 --- a/fern/01-guide/04-baml-basics/error-handling.mdx +++ b/fern/01-guide/04-baml-basics/error-handling.mdx @@ -28,7 +28,7 @@ except BamlValidationError as e: ```typescript TypeScript import { b } from './baml_client' // For catching parsing errors, you can import this -import { BamlValidationError } from '@boundaryml/baml' +import { BamlValidationError, BamlClientFinishReasonError } from '@boundaryml/baml' // The rest of the BAML errors contain a string that is prefixed with: // "BamlError:" // Subclasses are sequentially appended to the string. @@ -42,7 +42,7 @@ async function example() { try { await b.CallFunctionThatRaisesError() } catch (e) { - if (e instanceof BamlValidationError) { + if (e instanceof BamlValidationError || e instanceof BamlClientFinishReasonError) { // You should be lenient to these fields missing. // The original prompt sent to the LLM console.log(e.prompt) @@ -116,6 +116,41 @@ Common status codes are: - 500: Internal Server Error + +#### BamlClientFinishReasonError + +Subclass of `BamlClientError`. + +Raised when the finish reason of the LLM response is not allowed. + + + The finish reason of the LLM response. + + + + An error message. + + + + The original prompt that was sent to the LLM, formatted as a plain string. Images sent as base64-encoded strings are not serialized into this field. + + + + The raw text from the LLM that failed to parse into the expected return type of a function. + + ### BamlValidationError Subclass of `BamlError`. diff --git a/fern/01-guide/05-baml-advanced/client-registry.mdx b/fern/01-guide/05-baml-advanced/client-registry.mdx index f89f23ee6..77dbb96c4 100644 --- a/fern/01-guide/05-baml-advanced/client-registry.mdx +++ b/fern/01-guide/05-baml-advanced/client-registry.mdx @@ -36,11 +36,11 @@ import { ClientRegistry } from '@boundaryml/baml' async function run() { const cr = new ClientRegistry() // Creates a new client - cr.addLlmClient({ name: 'MyAmazingClient', provider: 'openai', options: { + cr.addLlmClient('MyAmazingClient', 'openai', { model: "gpt-4o", temperature: 0.7, api_key: process.env.OPENAI_API_KEY - }}) + }) // Sets MyAmazingClient as the primary client cr.setPrimary('MyAmazingClient') @@ -60,9 +60,9 @@ def run # Creates a new client cr.add_llm_client( - name: 'MyAmazingClient', - provider: 'openai', - options: { + 'MyAmazingClient', + 'openai', + { model: 'gpt-4o', temperature: 0.7, api_key: ENV['OPENAI_API_KEY'] diff --git a/fern/03-reference/baml/clients/providers/anthropic.mdx b/fern/03-reference/baml/clients/providers/anthropic.mdx index 33cbdec65..f618ea4fe 100644 --- a/fern/03-reference/baml/clients/providers/anthropic.mdx +++ b/fern/03-reference/baml/clients/providers/anthropic.mdx @@ -35,15 +35,6 @@ The options are passed through directly to the API, barring a few. Here's a shor The base URL for the API. **Default: `https://api.anthropic.com`** - - The default role for any prompts that don't specify a role. **Default: `system`** - - We don't have any checks for this field, you can pass any string you wish. - - Additional headers to send with the request. @@ -73,6 +64,8 @@ client MyClient { + + ## Forwarded options + + ## Forwarded options - - The default role for any prompts that don't specify a role. **Default: `system`** - - We don't have any checks for this field, you can pass any string you wish. - - Will be passed via a query parameter `api-version`. @@ -101,6 +92,7 @@ client MyClient { + ## Forwarded options - - The default role for any prompts that don't specify a role. **Default: `user`** - - We don't have any checks for this field, you can pass any string you wish. - - MyClient { + + ## Forwarded options MyClient { + provider "openai-generic" + options { + base_url "http://0.0.0.0:4000" + api_key env.LITELLM_API_KEY + model "gpt-4o" + } +} +``` diff --git a/fern/03-reference/baml/clients/providers/ollama.mdx b/fern/03-reference/baml/clients/providers/ollama.mdx index 9821db860..776a8457e 100644 --- a/fern/03-reference/baml/clients/providers/ollama.mdx +++ b/fern/03-reference/baml/clients/providers/ollama.mdx @@ -35,15 +35,6 @@ The options are passed through directly to the API, barring a few. Here's a shor Note the `/v1` at the end of the URL. See [Ollama's OpenAI compatability](https://ollama.com/blog/openai-compatibility) - - The default role for any prompts that don't specify a role. **Default: `system`** - - We don't have any checks for this field, you can pass any string you wish. - - Additional headers to send with the request. diff --git a/fern/03-reference/baml/clients/providers/openai-generic.mdx b/fern/03-reference/baml/clients/providers/openai-generic.mdx index a6bd929bb..6f98bb42a 100644 --- a/fern/03-reference/baml/clients/providers/openai-generic.mdx +++ b/fern/03-reference/baml/clients/providers/openai-generic.mdx @@ -27,14 +27,6 @@ client MyClient { **Default: `https://api.openai.com/v1`** - - The default role for any prompts that don't specify a role. - - We don't do any validation of this field, so you can pass any string you wish. - - **Default: `system`** - - Will be used to build the `Authorization` header, like so: `Authorization: Bearer $api_key` If `api_key` is not set, or is set to an empty string, the `Authorization` header will not be sent. @@ -68,6 +60,8 @@ client MyClient { + + ## Forwarded options - - The default role for any prompts that don't specify a role. - - We don't do any validation of this field, so you can pass any string you wish. - - **Default: `system`** - - Additional headers to send with the request. @@ -76,6 +68,7 @@ client MyClient { + ## Forwarded options diff --git a/fern/03-reference/baml/clients/providers/vertex.mdx b/fern/03-reference/baml/clients/providers/vertex.mdx index b8496dc5d..f56f5a107 100644 --- a/fern/03-reference/baml/clients/providers/vertex.mdx +++ b/fern/03-reference/baml/clients/providers/vertex.mdx @@ -214,6 +214,8 @@ client MyClient { + + ## Forwarded options +When running VSCode from a remote machine, you likely need to set this to `false`. + + +Many LLM providers don't accept requests from the browser. This setting enables a proxy that runs in the background and forwards requests to the LLM provider. + +## Usage + +```json settings.json +{ + "baml.enablePlaygroundProxy": false +} +``` + diff --git a/fern/docs.yml b/fern/docs.yml index 28ebd1fc0..db997dd79 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -444,6 +444,8 @@ navigation: path: 03-reference/vscode-ext/generateCodeOnSave.mdx - page: baml.restartTSServerOnSave path: 03-reference/vscode-ext/restartTSServerOnSave.mdx + - page: baml.enablePlaygroundProxy + path: 03-reference/vscode-ext/enablePlaygroundProxy.mdx - section: Boundary Extraction API contents: - api: API Reference diff --git a/fern/snippets/finish-reason.mdx b/fern/snippets/finish-reason.mdx new file mode 100644 index 000000000..68e24d219 --- /dev/null +++ b/fern/snippets/finish-reason.mdx @@ -0,0 +1,51 @@ + + Which finish reasons are allowed? **Default: `null`** + + Will raise a `BamlClientFinishReasonError` if the finish reason is not in the allow list. See [Exceptions](/guide/baml-basics/error-handling#bamlclientfinishreasonerror) for more details. + + Note, only one of `finish_reason_allow_list` or `finish_reason_deny_list` can be set. + + For example you can set this to `["stop"]` to only allow the stop finish reason, all other finish reasons (e.g. `length`) will treated as failures that PREVENT fallbacks and retries (similar to parsing errors). + + Then in your code you can use something like: + ```baml + client MyClient { + provider "openai" + options { + model "gpt-4o-mini" + api_key env.OPENAI_API_KEY + // Finish reason allow list will only allow the stop finish reason + finish_reason_allow_list ["stop"] + } + } + ``` + + + + Which finish reasons are denied? **Default: `null`** + + Will raise a `BamlClientFinishReasonError` if the finish reason is in the deny list. See [Exceptions](/guide/baml-basics/error-handling#bamlclientfinishreasonerror) for more details. + + Note, only one of `finish_reason_allow_list` or `finish_reason_deny_list` can be set. + + For example you can set this to `["length"]` to stop the function from continuing if the finish reason is `length`. (e.g. LLM was cut off because it was too long). + + Then in your code you can use something like: + ```baml + client MyClient { + provider "openai" + options { + model "gpt-4o-mini" + api_key env.OPENAI_API_KEY + // Finish reason deny list will allow all finish reasons except length + finish_reason_deny_list ["length"] + } + } + ``` +