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 2483569fe..bae08a9c7 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 @@ -335,26 +335,21 @@ impl RequestBuilder for AnthropicClient { async fn build_request( &self, prompt: either::Either<&String, &Vec>, + allow_proxy: bool, stream: bool, ) -> Result { + let destination_url = if allow_proxy { + self.properties + .proxy_url + .as_ref() + .unwrap_or(&self.properties.base_url) + } else { + &self.properties.base_url + }; let mut req = self.client.post(if prompt.is_left() { - format!( - "{}/v1/complete", - self.properties - .proxy_url - .as_ref() - .unwrap_or(&self.properties.base_url) - .clone() - ) + format!("{}/v1/complete", destination_url) } else { - format!( - "{}/v1/messages", - self.properties - .proxy_url - .as_ref() - .unwrap_or(&self.properties.base_url) - .clone() - ) + format!("{}/v1/messages", destination_url) }); for (key, value) in &self.properties.headers { @@ -364,12 +359,9 @@ impl RequestBuilder for AnthropicClient { req = req.header("x-api-key", key); } - req = req.header("baml-original-url", self.properties.base_url.as_str()); - req = req.header( - "baml-render-url", - format!("{}/v1/messages", self.properties.base_url), - ); - + if allow_proxy { + req = req.header("baml-original-url", self.properties.base_url.as_str()); + } let mut body = json!(self.properties.properties); let body_obj = body.as_object_mut().unwrap(); match prompt { @@ -384,7 +376,6 @@ impl RequestBuilder for AnthropicClient { if stream { body_obj.insert("stream".into(), true.into()); } - log::debug!("Request body: {:#?}", body); Ok(req.json(&body)) } diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/aws/aws_client.rs b/engine/baml-runtime/src/internal/llm_client/primitive/aws/aws_client.rs index cd80b9f5e..bec74bcac 100644 --- a/engine/baml-runtime/src/internal/llm_client/primitive/aws/aws_client.rs +++ b/engine/baml-runtime/src/internal/llm_client/primitive/aws/aws_client.rs @@ -205,6 +205,7 @@ impl AwsClient { fn build_request( &self, ctx: &RuntimeContext, + chat_messages: &Vec, ) -> Result { let mut system_message = None; @@ -511,7 +512,7 @@ impl TryInto for AwsChatMessage<'_> { ChatMessagePart::Image(media) | ChatMessagePart::Audio(media) => match media { BamlMedia::Url(_, _) => { anyhow::bail!( - "BAML internal error: media URL should have been resolved to base64" + "BAML internal error (AWSBedrock): media URL should have been resolved to base64" ) } BamlMedia::Base64(BamlMediaType::Image, media) => { 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 index 85dedf265..0cc977d5b 100644 --- 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 @@ -276,6 +276,7 @@ impl RequestBuilder for GoogleClient { async fn build_request( &self, prompt: either::Either<&String, &Vec>, + allow_proxy: bool, stream: bool, ) -> Result { let mut should_stream = "generateContent"; @@ -283,27 +284,38 @@ impl RequestBuilder for GoogleClient { should_stream = "streamGenerateContent?alt=sse"; } - let baml_original_url = format!( - "{}/models/{}:{}", - self.properties.base_url, - self.properties.model_id.as_ref().unwrap_or(&"".to_string()), - should_stream - ); - - let mut req = self.client.post( + let destination_url = if allow_proxy { self.properties .proxy_url .as_ref() - .unwrap_or(&baml_original_url) - .clone(), - ); + .unwrap_or(&"".to_string()) + .clone() + } else { + format!( + "{}/models/{}:{}", + self.properties.base_url, + self.properties.model_id.as_ref().unwrap_or(&"".to_string()), + should_stream + ) + }; + + let mut req = self.client.post(destination_url); for (key, value) in &self.properties.headers { req = req.header(key, value); } - req = req.header("baml-original-url", baml_original_url.clone()); - req = req.header("baml-render-url", baml_original_url); + if allow_proxy { + let baml_original_url = format!( + "{}/models/{}:{}", + self.properties.base_url, + self.properties.model_id.as_ref().unwrap_or(&"".to_string()), + should_stream + ); + + req = req.header("baml-original-url", baml_original_url.clone()); + } + req = req.header( "x-goog-api-key", self.properties diff --git a/engine/baml-runtime/src/internal/llm_client/primitive/openai/openai_client.rs b/engine/baml-runtime/src/internal/llm_client/primitive/openai/openai_client.rs index 8a956e66b..b7fde1766 100644 --- a/engine/baml-runtime/src/internal/llm_client/primitive/openai/openai_client.rs +++ b/engine/baml-runtime/src/internal/llm_client/primitive/openai/openai_client.rs @@ -214,26 +214,22 @@ impl RequestBuilder for OpenAIClient { async fn build_request( &self, prompt: either::Either<&String, &Vec>, + allow_proxy: bool, + stream: bool, ) -> Result { + let destination_url = if allow_proxy { + self.properties + .proxy_url + .as_ref() + .unwrap_or(&self.properties.base_url) + } else { + &self.properties.base_url + }; let mut req = self.client.post(if prompt.is_left() { - format!( - "{}/completions", - self.properties - .proxy_url - .as_ref() - .unwrap_or(&self.properties.base_url) - .clone() - ) + format!("{}/completions", destination_url) } else { - format!( - "{}/chat/completions", - self.properties - .proxy_url - .as_ref() - .unwrap_or(&self.properties.base_url) - .clone() - ) + format!("{}/chat/completions", destination_url) }); if !self.properties.query_params.is_empty() { @@ -246,12 +242,11 @@ impl RequestBuilder for OpenAIClient { if let Some(key) = &self.properties.api_key { req = req.bearer_auth(key) } - req = req.header("baml-original-url", self.properties.base_url.as_str()); - req = req.header( - "baml-render-url", - format!("{}/chat/completions", self.properties.base_url), - ); + if allow_proxy { + req = req.header("baml-original-url", self.properties.base_url.as_str()); + } + let mut body = json!(self.properties.properties); let body_obj = body.as_object_mut().unwrap(); match prompt { 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 bc31c9f53..c8a769307 100644 --- a/engine/baml-runtime/src/internal/llm_client/primitive/request.rs +++ b/engine/baml-runtime/src/internal/llm_client/primitive/request.rs @@ -12,6 +12,7 @@ pub trait RequestBuilder { async fn build_request( &self, prompt: either::Either<&String, &Vec>, + allow_proxy: bool, stream: bool, ) -> Result; @@ -38,7 +39,7 @@ pub async fn make_request( log::debug!("Making request using client {}", client.context().name); let req = match client - .build_request(prompt, stream) + .build_request(prompt, true, stream) .await .context("Failed to build request") { diff --git a/engine/baml-runtime/src/internal/llm_client/traits/mod.rs b/engine/baml-runtime/src/internal/llm_client/traits/mod.rs index cb07775ef..d652f5c53 100644 --- a/engine/baml-runtime/src/internal/llm_client/traits/mod.rs +++ b/engine/baml-runtime/src/internal/llm_client/traits/mod.rs @@ -13,7 +13,7 @@ use super::{primitive::request::RequestBuilder, LLMResponse, ModelFeatures}; use crate::{internal::prompt_renderer::PromptRenderer, RuntimeContext}; use baml_types::{BamlMedia, BamlMediaType, BamlValue, MediaBase64}; use base64::{prelude::BASE64_STANDARD, Engine}; -use futures::stream::{StreamExt, TryStreamExt}; +use futures::stream::StreamExt; use infer; use internal_baml_core::ir::repr::IntermediateRepr; use internal_baml_jinja::{ChatMessagePart, RenderedChatMessage}; @@ -83,104 +83,10 @@ where async fn single_call(&self, ctx: &RuntimeContext, prompt: &RenderedPrompt) -> LLMResponse { if self.model_features().resolve_media_urls { if let RenderedPrompt::Chat(ref chat) = prompt { - let messages_result = futures::stream::iter(chat.iter().map(|p| { - let new_parts = p - .parts - .iter() - .map(|part| async move { - match part { - ChatMessagePart::Image(BamlMedia::Url(_, media_url)) - | ChatMessagePart::Audio(BamlMedia::Url(_, media_url)) => { - let (base64, mime_type) = if media_url.url.starts_with("data:") { - let parts: Vec<&str> = - media_url.url.splitn(2, ',').collect(); - let base64 = parts.get(1).unwrap().to_string(); - let prefix = parts.get(0).unwrap(); - let mime_type = - prefix.splitn(2, ':').next().unwrap().to_string() - .split('/').last().unwrap().to_string(); - - (base64, mime_type) - } else { - let client = reqwest::Client::new(); - let response = match client.get(&media_url.url) - // NB(sam): this would workaround CORS issues, but https://github.com/seanmonstar/reqwest/issues/1401 - //.fetch_mode_no_cors() - .send().await - { - Ok(response) => response, - Err(e) => { - return Err(LLMResponse::OtherFailure( - format!("Failed to fetch image due to CORS issue: {e:?}") - )) - } // replace with your error conversion logic - }; - let bytes = match response.bytes().await { - Ok(bytes) => bytes, - Err(e) => { - return Err(LLMResponse::OtherFailure( - e.to_string(), - )); - } // replace with your error conversion logic - }; - let base64 = BASE64_STANDARD.encode(&bytes); - let inferred_type = infer::get(&bytes); - let mime_type = inferred_type.map_or_else( - || "application/octet-stream".into(), - |t| t.extension().into(), - ); - (base64, mime_type) - }; - - Ok(if matches!(part, ChatMessagePart::Image(_)) { - ChatMessagePart::Image(BamlMedia::Base64( - BamlMediaType::Image, - MediaBase64 { - base64: base64, - media_type: format!("image/{}", mime_type), - }, - )) - } else { - ChatMessagePart::Audio(BamlMedia::Base64( - BamlMediaType::Audio, - MediaBase64 { - base64: base64, - media_type: format!("audio/{}", mime_type), - }, - )) - }) - } - _ => Ok(part.clone()), - } - }) - .collect::>(); - async move { - let new_parts = futures::stream::iter(new_parts) - .then(|f| f) - .collect::>() - .await; - - let new_parts = new_parts.into_iter().collect::, _>>()?; - - Ok::<_, anyhow::Error>(RenderedChatMessage { - role: p.role.clone(), - parts: new_parts, - }) - } - })) - .then(|f| f) - .collect::>() - .await - .into_iter() - .collect::, _>>(); - - let messages = match messages_result { - Ok(messages) => messages, - Err(e) => { - return LLMResponse::OtherFailure(format!("Error occurred: {}", e)); - } - }; - return self.chat(ctx, &messages).await; + match process_media_urls(ctx, chat).await { + Ok(messages) => return self.chat(ctx, &messages).await, + Err(e) => return LLMResponse::OtherFailure(format!("Error occurred: {}", e)), + } } } @@ -203,102 +109,9 @@ where ) -> Result, LLMResponse> { if self.model_features().resolve_media_urls { if let RenderedPrompt::Chat(ref chat) = prompt { - let messages_result = futures::stream::iter(chat.iter().map(|p| { - let new_parts = p - .parts - .iter() - .map(|part| async move { - match part { - ChatMessagePart::Image(BamlMedia::Url(_, media_url)) - | ChatMessagePart::Audio(BamlMedia::Url(_, media_url)) => { - let mut base64 = "".to_string(); - let mut mime_type = "".to_string(); - if media_url.url.starts_with("data:") { - let parts: Vec<&str> = - media_url.url.splitn(2, ',').collect(); - base64 = parts.get(1).unwrap().to_string(); - let prefix = parts.get(0).unwrap(); - mime_type = - prefix.splitn(2, ':').next().unwrap().to_string(); - mime_type = - mime_type.split('/').last().unwrap().to_string(); - } else { - let client = reqwest::Client::new(); - let response = match client.get(&media_url.url).send().await - { - Ok(response) => response, - Err(e) => { - return Err(LLMResponse::OtherFailure( - "Failed to fetch image due to CORS issue" - .to_string(), - )) - } // replace with your error conversion logic - }; - let bytes = match response.bytes().await { - Ok(bytes) => bytes, - Err(e) => { - return Err(LLMResponse::OtherFailure( - e.to_string(), - )) - } // replace with your error conversion logic - }; - base64 = BASE64_STANDARD.encode(&bytes); - let inferred_type = infer::get(&bytes); - mime_type = inferred_type.map_or_else( - || "application/octet-stream".into(), - |t| t.extension().into(), - ); - } - - Ok(if matches!(part, ChatMessagePart::Image(_)) { - ChatMessagePart::Image(BamlMedia::Base64( - BamlMediaType::Image, - MediaBase64 { - base64: base64, - media_type: format!("image/{}", mime_type), - }, - )) - } else { - ChatMessagePart::Audio(BamlMedia::Base64( - BamlMediaType::Audio, - MediaBase64 { - base64: base64, - media_type: format!("audio/{}", mime_type), - }, - )) - }) - } - _ => Ok(part.clone()), - } - }) - .collect::>(); - async move { - let new_parts = futures::stream::iter(new_parts) - .then(|f| f) - .collect::>() - .await; - - let new_parts = new_parts.into_iter().collect::, _>>()?; - - Ok::<_, anyhow::Error>(RenderedChatMessage { - role: p.role.clone(), - parts: new_parts, - }) - } - })) - .then(|f| f) - .collect::>() - .await - .into_iter() - .collect::, _>>(); - - let messages = match messages_result { - Ok(messages) => messages, - Err(e) => { - return Err(LLMResponse::OtherFailure(format!("Error occurred: {}", e))); - } - }; - return Ok(messages); + return process_media_urls(ctx, chat) + .await + .map_err(|e| LLMResponse::OtherFailure(format!("Error occurred: {}", e))); } } @@ -406,34 +219,26 @@ where let chat_messages = self.curl_call(ctx, &rendered_prompt).await?; let request_builder = self - .build_request(either::Right(&chat_messages), stream) + .build_request(either::Right(&chat_messages), false, stream) .await?; let mut request = request_builder.build()?; let url_header_value = { - let headers = request.headers_mut(); - let url_header_value = headers - .get("baml-render-url") - .ok_or(anyhow::anyhow!("Missing header 'baml-render-url'"))?; + let url_header_value = request.url(); url_header_value.to_owned() }; - let url_str = url_header_value - .to_str() - .map_err(|_| anyhow::anyhow!("Invalid header 'baml-render-url'"))?; - let new_url = Url::from_str(url_str)?; - *request.url_mut() = new_url; + let url_str = url_header_value.to_string(); { let headers = request.headers_mut(); headers.remove("baml-original-url"); - headers.remove("baml-render-url"); } let body = request .body() .map(|b| b.as_bytes().unwrap_or_default().to_vec()) .unwrap_or_default(); // Add this line to handle the Option - let request_str = to_curl_command(url_str, "POST", request.headers(), body); + let request_str = to_curl_command(&url_str, "POST", request.headers(), body); Ok(request_str) } @@ -471,107 +276,12 @@ where async fn stream(&self, ctx: &RuntimeContext, prompt: &RenderedPrompt) -> StreamResponse { if self.model_features().resolve_media_urls { if let RenderedPrompt::Chat(ref chat) = prompt { - let messages = futures::stream::iter(chat.iter().map(|p| { - let new_parts = p - .parts - .iter() - .map(|part| async move { - match part { - ChatMessagePart::Image(BamlMedia::Url(_, media_url)) - | ChatMessagePart::Audio(BamlMedia::Url(_, media_url)) => { - let mut base64 = "".to_string(); - let mut mime_type = "".to_string(); - if media_url.url.starts_with("data:") { - let parts: Vec<&str> = - media_url.url.splitn(2, ',').collect(); - base64 = parts.get(1).unwrap().to_string(); - let prefix = parts.get(0).unwrap(); - mime_type = prefix - .splitn(2, ':') - .last() - .unwrap() // Get the part after "data:" - .split('/') - .last() - .unwrap() // Get the part after "image/" - .split(';') - .next() - .unwrap() // Get the part before ";base64" - .to_string(); - } else { - let client = reqwest::Client::new(); - let response = match client - .get(&media_url.url) - // NB(sam): this would workaround CORS issues, but https://github.com/seanmonstar/reqwest/issues/1401 - //.fetch_mode_no_cors() - .send() - .await - { - Ok(response) => response, - Err(e) => { - return Err(LLMResponse::OtherFailure( - "Failed to fetch image due to CORS issue" - .to_string(), - )) - } // replace with your error conversion logic - }; - let bytes = match response.bytes().await { - Ok(bytes) => bytes, - Err(e) => { - return Err(LLMResponse::OtherFailure( - e.to_string(), - )) - } // replace with your error conversion logic - }; - base64 = BASE64_STANDARD.encode(&bytes); - let inferred_type = infer::get(&bytes); - mime_type = inferred_type.map_or_else( - || "application/octet-stream".into(), - |t| t.extension().into(), - ); - } - Ok(if matches!(part, ChatMessagePart::Image(_)) { - ChatMessagePart::Image(BamlMedia::Base64( - BamlMediaType::Image, - MediaBase64 { - base64: base64, - media_type: format!("image/{}", mime_type), - }, - )) - } else { - ChatMessagePart::Audio(BamlMedia::Base64( - BamlMediaType::Audio, - MediaBase64 { - base64: base64, - media_type: format!("audio/{}", mime_type), - }, - )) - }) - } - _ => Ok(part.clone()), - } - }) - .collect::>(); - async move { - let new_parts = futures::stream::iter(new_parts) - .then(|f| f) - .collect::>() - .await; - - let new_parts = new_parts.into_iter().collect::, _>>()?; - - Ok(RenderedChatMessage { - role: p.role.clone(), - parts: new_parts, - }) + match process_media_urls(ctx, chat).await { + Ok(messages) => return self.stream_chat(ctx, &messages).await, + Err(e) => { + return Err(LLMResponse::OtherFailure(format!("Error occurred: {}", e))) } - })) - .then(|f| f) - .collect::>() - .await - .into_iter() - .collect::, _>>()?; - - return self.stream_chat(ctx, &messages).await; + } } } @@ -581,3 +291,122 @@ where } } } + +async fn process_media_urls( + ctx: &RuntimeContext, + chat: &Vec, +) -> Result, anyhow::Error> { + let messages_result = futures::stream::iter(chat.iter().map(|p| { + let new_parts = p + .parts + .iter() + .map(|part| async move { + match part { + ChatMessagePart::Image(BamlMedia::Url(_, media_url)) + | ChatMessagePart::Audio(BamlMedia::Url(_, media_url)) => { + let (base64, mime_type) = if media_url.url.starts_with("data:") { + let parts: Vec<&str> = media_url.url.splitn(2, ',').collect(); + let base64 = parts.get(1).unwrap().to_string(); + let prefix = parts.get(0).unwrap(); + let mime_type = prefix + .splitn(2, ':') + .next() + .unwrap() + .to_string() + .split('/') + .last() + .unwrap() + .to_string(); + + (base64, mime_type) + } else { + let response = match fetch_with_proxy( + &media_url.url, + ctx.env + .get("BOUNDARY_PROXY_URL") + .as_deref() + .map(|s| s.as_str()), + ) + .await + { + Ok(response) => response, + Err(e) => { + return Err(anyhow::anyhow!("Failed to fetch media: {e:?}")) + } + }; + let bytes = match response.bytes().await { + Ok(bytes) => bytes, + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to fetch media bytes: {e:?}" + )) + } + }; + let base64 = BASE64_STANDARD.encode(&bytes); + let inferred_type = infer::get(&bytes); + let mime_type = inferred_type.map_or_else( + || "application/octet-stream".into(), + |t| t.extension().into(), + ); + (base64, mime_type) + }; + + Ok(if matches!(part, ChatMessagePart::Image(_)) { + ChatMessagePart::Image(BamlMedia::Base64( + BamlMediaType::Image, + MediaBase64 { + base64: base64, + media_type: format!("image/{}", mime_type), + }, + )) + } else { + ChatMessagePart::Audio(BamlMedia::Base64( + BamlMediaType::Audio, + MediaBase64 { + base64: base64, + media_type: format!("audio/{}", mime_type), + }, + )) + }) + } + _ => Ok(part.clone()), + } + }) + .collect::>(); + async move { + let new_parts = futures::stream::iter(new_parts) + .then(|f| f) + .collect::>() + .await; + + let new_parts = new_parts.into_iter().collect::, _>>()?; + + Ok::<_, anyhow::Error>(RenderedChatMessage { + role: p.role.clone(), + parts: new_parts, + }) + } + })) + .then(|f| f) + .collect::>() + .await + .into_iter() + .collect::, _>>(); + + messages_result +} + +async fn fetch_with_proxy( + url: &str, + proxy_url: Option<&str>, +) -> Result { + let client = reqwest::Client::new(); + let mut request = if let Some(proxy) = proxy_url { + client.get(proxy).header("baml-original-url", url) + } else { + client.get(url) + }; + + let response = request.send().await?; + Ok(response) +} diff --git a/engine/baml-schema-wasm/src/runtime_wasm/runtime_prompt.rs b/engine/baml-schema-wasm/src/runtime_wasm/runtime_prompt.rs index 2be7177e1..9712a42fb 100644 --- a/engine/baml-schema-wasm/src/runtime_wasm/runtime_prompt.rs +++ b/engine/baml-schema-wasm/src/runtime_wasm/runtime_prompt.rs @@ -110,10 +110,6 @@ impl WasmPrompt { #[wasm_bindgen] pub fn as_chat(&self) -> Option> { if let RenderedPrompt::Chat(s) = &self.prompt { - log::info!( - "Chat role: {:?}", - s.iter().map(|m| m.role.clone()).collect::>() - ); Some( s.iter() .map(|m| WasmChatMessage { diff --git a/typescript/fiddle-frontend/app/globals.css b/typescript/fiddle-frontend/app/globals.css index dec2545ce..19d87e3fb 100644 --- a/typescript/fiddle-frontend/app/globals.css +++ b/typescript/fiddle-frontend/app/globals.css @@ -84,7 +84,7 @@ /* controls dropdown styling on rsjf */ .form-control { - @apply bg-vscode-input-background text-vscode-input-foreground rounded-sm; +@apply bg-vscode-input-background text-vscode-input-foreground rounded-sm; } .array-item { diff --git a/typescript/playground-common/src/baml_wasm_web/EventListener.tsx b/typescript/playground-common/src/baml_wasm_web/EventListener.tsx index a456644fd..80d9be506 100644 --- a/typescript/playground-common/src/baml_wasm_web/EventListener.tsx +++ b/typescript/playground-common/src/baml_wasm_web/EventListener.tsx @@ -346,6 +346,8 @@ export const availableFunctionsAtom = atom((get) => { return runtime.list_functions() }) +export const streamCurl = atom(true) + const asyncCurlAtom = atom(async (get) => { const runtime = get(selectedRuntimeAtom) const func = get(selectedFunctionAtom) @@ -360,10 +362,10 @@ const asyncCurlAtom = atom(async (get) => { .map((input) => [input.name, JSON.parse(input.value)]), ) try { - return await func.render_raw_curl(runtime, params, false) + return await func.render_raw_curl(runtime, params, get(streamCurl)) } catch (e) { console.error(e) - return 'Error rendering curl command' + return `${e}` } }) diff --git a/typescript/playground-common/src/shared/FunctionPanel.tsx b/typescript/playground-common/src/shared/FunctionPanel.tsx index 02b37a340..20e2c085b 100644 --- a/typescript/playground-common/src/shared/FunctionPanel.tsx +++ b/typescript/playground-common/src/shared/FunctionPanel.tsx @@ -1,7 +1,7 @@ /// Content once a function has been selected. import { useAppState } from './AppStateContext' import { useAtomValue, useSetAtom } from 'jotai' -import { renderPromptAtom, selectedFunctionAtom, curlAtom } from '../baml_wasm_web/EventListener' +import { renderPromptAtom, selectedFunctionAtom, curlAtom, streamCurl } from '../baml_wasm_web/EventListener' import TestResults from '../baml_wasm_web/test_uis/test_result' import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '../components/ui/resizable' import { TooltipProvider } from '../components/ui/tooltip' @@ -10,6 +10,7 @@ import FunctionTestSnippet from './TestSnippet' import { Copy } from 'lucide-react' import { Button } from '../components/ui/button' import { CheckboxHeader } from './CheckboxHeader' +import { Switch } from '../components/ui/switch' import CustomErrorBoundary from '../utils/ErrorFallback' const handleCopy = (text: string) => () => { navigator.clipboard.writeText(text) @@ -20,10 +21,18 @@ const CurlSnippet: React.FC = () => { return (
-
+
+ diff --git a/typescript/vscode-ext/packages/vscode/src/extension.ts b/typescript/vscode-ext/packages/vscode/src/extension.ts index 6ab474f90..7cb94b5e8 100644 --- a/typescript/vscode-ext/packages/vscode/src/extension.ts +++ b/typescript/vscode-ext/packages/vscode/src/extension.ts @@ -204,7 +204,7 @@ export function activate(context: vscode.ExtensionContext) { let originalUrl = req.headers['baml-original-url'] if (typeof originalUrl === 'string') { delete req.headers['baml-original-url'] - delete req.headers['baml-render-url'] + req.headers['origin'] = `http://localhost:${port}` // Ensure the URL does not end with a slash