feat: ACP providers for claude code and codex (#6605)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -4,15 +4,12 @@
|
||||
|
||||
#[path = "../fixtures/mod.rs"]
|
||||
pub mod fixtures;
|
||||
use fixtures::{
|
||||
initialize_agent, Connection, OpenAiFixture, PermissionDecision, Session, TestConnectionConfig,
|
||||
};
|
||||
use fixtures::{Connection, OpenAiFixture, PermissionDecision, Session, TestConnectionConfig};
|
||||
use fs_err as fs;
|
||||
use goose::config::base::CONFIG_YAML_NAME;
|
||||
use goose::config::GooseMode;
|
||||
use goose::providers::provider_registry::ProviderConstructor;
|
||||
use goose_acp::server::GooseAcpAgent;
|
||||
use goose_test_support::{ExpectedSessionId, McpFixture, FAKE_CODE, TEST_MODEL};
|
||||
use goose_test_support::{ExpectedSessionId, McpFixture, FAKE_CODE, TEST_IMAGE_B64, TEST_MODEL};
|
||||
use sacp::schema::{McpServer, McpServerHttp, ModelId, ToolCallStatus};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -57,29 +54,20 @@ pub async fn run_config_mcp<C: Connection>() {
|
||||
expected_session_id.assert_matches(&session.session_id().0);
|
||||
}
|
||||
|
||||
pub async fn run_initialize_without_provider() {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
|
||||
pub async fn run_initialize_doesnt_hit_provider<C: Connection>() {
|
||||
let provider_factory: ProviderConstructor =
|
||||
Arc::new(|_, _| Box::pin(async { Err(anyhow::anyhow!("no provider configured")) }));
|
||||
|
||||
let agent = Arc::new(
|
||||
GooseAcpAgent::new(
|
||||
provider_factory,
|
||||
vec![],
|
||||
temp_dir.path().to_path_buf(),
|
||||
temp_dir.path().to_path_buf(),
|
||||
GooseMode::Auto,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
let openai = OpenAiFixture::new(vec![], ExpectedSessionId::default()).await;
|
||||
let config = TestConnectionConfig {
|
||||
provider_factory: Some(provider_factory),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let resp = initialize_agent(agent).await;
|
||||
assert!(!resp.auth_methods.is_empty());
|
||||
assert!(resp
|
||||
.auth_methods
|
||||
let conn = C::new(config, openai).await;
|
||||
assert!(!conn.auth_methods().is_empty());
|
||||
assert!(conn
|
||||
.auth_methods()
|
||||
.iter()
|
||||
.any(|m| &*m.id.0 == "goose-provider"));
|
||||
}
|
||||
@@ -334,6 +322,33 @@ pub async fn run_prompt_image<C: Connection>() {
|
||||
expected_session_id.assert_matches(&session.session_id().0);
|
||||
}
|
||||
|
||||
pub async fn run_prompt_image_attachment<C: Connection>() {
|
||||
let expected_session_id = ExpectedSessionId::default();
|
||||
let openai = OpenAiFixture::new(
|
||||
vec![(
|
||||
r#""type":"image_url""#.into(),
|
||||
include_str!("../test_data/openai_image_attachment.txt"),
|
||||
)],
|
||||
expected_session_id.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let mut conn = C::new(TestConnectionConfig::default(), openai).await;
|
||||
let (mut session, _) = conn.new_session().await;
|
||||
expected_session_id.set(session.session_id().0.to_string());
|
||||
|
||||
let output = session
|
||||
.prompt_with_image(
|
||||
"Describe what you see in this image",
|
||||
TEST_IMAGE_B64,
|
||||
"image/png",
|
||||
PermissionDecision::Cancel,
|
||||
)
|
||||
.await;
|
||||
assert!(output.text.contains("Hello Goose!"));
|
||||
expected_session_id.assert_matches(&session.session_id().0);
|
||||
}
|
||||
|
||||
pub async fn run_prompt_mcp<C: Connection>() {
|
||||
let expected_session_id = ExpectedSessionId::default();
|
||||
let mcp = McpFixture::new(Some(expected_session_id.clone())).await;
|
||||
|
||||
+13
-49
@@ -3,19 +3,17 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use fs_err as fs;
|
||||
pub use goose::acp::{map_permission_response, PermissionDecision, PermissionMapping};
|
||||
use goose::builtin_extension::register_builtin_extensions;
|
||||
use goose::config::{GooseMode, PermissionManager};
|
||||
use goose::providers::api_client::{ApiClient, AuthMethod};
|
||||
use goose::providers::api_client::{ApiClient, AuthMethod as ApiAuthMethod};
|
||||
use goose::providers::base::Provider;
|
||||
use goose::providers::openai::OpenAiProvider;
|
||||
use goose::providers::provider_registry::ProviderConstructor;
|
||||
use goose::session_context::SESSION_ID_HEADER;
|
||||
use goose_acp::server::{serve, GooseAcpAgent};
|
||||
use goose_test_support::{ExpectedSessionId, TEST_MODEL};
|
||||
use sacp::schema::{
|
||||
McpServer, PermissionOptionKind, RequestPermissionOutcome, RequestPermissionRequest,
|
||||
RequestPermissionResponse, SelectedPermissionOutcome, SessionModelState, ToolCallStatus,
|
||||
};
|
||||
use sacp::schema::{AuthMethod, McpServer, SessionModelState, ToolCallStatus};
|
||||
use std::collections::VecDeque;
|
||||
use std::future::Future;
|
||||
use std::path::Path;
|
||||
@@ -26,49 +24,6 @@ use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
|
||||
use wiremock::matchers::{method, path};
|
||||
use wiremock::{Mock, MockServer, ResponseTemplate};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum PermissionDecision {
|
||||
AllowAlways,
|
||||
AllowOnce,
|
||||
RejectOnce,
|
||||
RejectAlways,
|
||||
Cancel,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PermissionMapping;
|
||||
|
||||
pub fn map_permission_response(
|
||||
_mapping: &PermissionMapping,
|
||||
req: &RequestPermissionRequest,
|
||||
decision: PermissionDecision,
|
||||
) -> RequestPermissionResponse {
|
||||
let outcome = match decision {
|
||||
PermissionDecision::Cancel => RequestPermissionOutcome::Cancelled,
|
||||
PermissionDecision::AllowAlways => select_option(req, PermissionOptionKind::AllowAlways),
|
||||
PermissionDecision::AllowOnce => select_option(req, PermissionOptionKind::AllowOnce),
|
||||
PermissionDecision::RejectOnce => select_option(req, PermissionOptionKind::RejectOnce),
|
||||
PermissionDecision::RejectAlways => select_option(req, PermissionOptionKind::RejectAlways),
|
||||
};
|
||||
|
||||
RequestPermissionResponse::new(outcome)
|
||||
}
|
||||
|
||||
fn select_option(
|
||||
req: &RequestPermissionRequest,
|
||||
kind: PermissionOptionKind,
|
||||
) -> RequestPermissionOutcome {
|
||||
req.options
|
||||
.iter()
|
||||
.find(|opt| opt.kind == kind)
|
||||
.map(|opt| {
|
||||
RequestPermissionOutcome::Selected(SelectedPermissionOutcome::new(
|
||||
opt.option_id.clone(),
|
||||
))
|
||||
})
|
||||
.unwrap_or(RequestPermissionOutcome::Cancelled)
|
||||
}
|
||||
|
||||
pub struct OpenAiFixture {
|
||||
_server: MockServer,
|
||||
base_url: String,
|
||||
@@ -211,7 +166,7 @@ pub async fn spawn_acp_server_in_process(
|
||||
let base_url = base_url.clone();
|
||||
Box::pin(async move {
|
||||
let api_client =
|
||||
ApiClient::new(base_url, AuthMethod::BearerToken("test-key".to_string()))
|
||||
ApiClient::new(base_url, ApiAuthMethod::BearerToken("test-key".to_string()))
|
||||
.unwrap();
|
||||
let provider: Arc<dyn Provider> =
|
||||
Arc::new(OpenAiProvider::new(api_client, model_config));
|
||||
@@ -273,6 +228,7 @@ pub trait Connection: Sized {
|
||||
&mut self,
|
||||
session_id: &str,
|
||||
) -> (Self::Session, Option<SessionModelState>);
|
||||
fn auth_methods(&self) -> &[AuthMethod];
|
||||
fn reset_openai(&self);
|
||||
fn reset_permissions(&self);
|
||||
}
|
||||
@@ -281,6 +237,13 @@ pub trait Connection: Sized {
|
||||
pub trait Session {
|
||||
fn session_id(&self) -> &sacp::schema::SessionId;
|
||||
async fn prompt(&mut self, text: &str, decision: PermissionDecision) -> TestOutput;
|
||||
async fn prompt_with_image(
|
||||
&mut self,
|
||||
text: &str,
|
||||
image_b64: &str,
|
||||
mime_type: &str,
|
||||
decision: PermissionDecision,
|
||||
) -> TestOutput;
|
||||
async fn set_model(&self, model_id: &str);
|
||||
}
|
||||
|
||||
@@ -332,4 +295,5 @@ pub async fn initialize_agent(agent: Arc<GooseAcpAgent>) -> sacp::schema::Initia
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub mod provider;
|
||||
pub mod server;
|
||||
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
use super::{
|
||||
spawn_acp_server_in_process, Connection, OpenAiFixture, PermissionDecision, Session,
|
||||
TestConnectionConfig, TestOutput,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use futures::StreamExt;
|
||||
use goose::acp::{AcpProvider, AcpProviderConfig, PermissionMapping};
|
||||
use goose::config::PermissionManager;
|
||||
use goose::conversation::message::{ActionRequiredData, Message, MessageContent};
|
||||
use goose::model::ModelConfig;
|
||||
use goose::permission::permission_confirmation::PrincipalType;
|
||||
use goose::permission::{Permission, PermissionConfirmation};
|
||||
use goose::providers::base::Provider;
|
||||
use goose_test_support::TEST_MODEL;
|
||||
use sacp::schema::{AuthMethod, SessionModelState, ToolCallStatus};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct ClientToProviderConnection {
|
||||
provider: Arc<Mutex<AcpProvider>>,
|
||||
permission_manager: Arc<PermissionManager>,
|
||||
auth_methods: Vec<AuthMethod>,
|
||||
session_counter: usize,
|
||||
_openai: OpenAiFixture,
|
||||
_temp_dir: Option<tempfile::TempDir>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct ClientToProviderSession {
|
||||
provider: Arc<Mutex<AcpProvider>>,
|
||||
acp_session_id: sacp::schema::SessionId,
|
||||
session_id: String,
|
||||
}
|
||||
|
||||
impl ClientToProviderSession {
|
||||
#[allow(dead_code)]
|
||||
async fn send_message(&mut self, message: Message, decision: PermissionDecision) -> TestOutput {
|
||||
let session_id = self.session_id.clone();
|
||||
let provider = self.provider.lock().await;
|
||||
let model_config = provider.get_model_config();
|
||||
let mut stream = provider
|
||||
.stream(&model_config, &session_id, "", &[message], &[])
|
||||
.await
|
||||
.unwrap();
|
||||
let mut text = String::new();
|
||||
let mut tool_error = false;
|
||||
let mut saw_tool = false;
|
||||
|
||||
while let Some(item) = stream.next().await {
|
||||
let (msg, _) = item.unwrap();
|
||||
if let Some(msg) = msg {
|
||||
for content in msg.content {
|
||||
match content {
|
||||
MessageContent::Text(t) => {
|
||||
text.push_str(&t.text);
|
||||
}
|
||||
MessageContent::ToolResponse(resp) => {
|
||||
saw_tool = true;
|
||||
if let Ok(result) = resp.tool_result {
|
||||
tool_error |= result.is_error.unwrap_or(false);
|
||||
}
|
||||
}
|
||||
MessageContent::ActionRequired(action) => {
|
||||
if let ActionRequiredData::ToolConfirmation { id, .. } = action.data {
|
||||
saw_tool = true;
|
||||
tool_error |= decision.should_record_rejection();
|
||||
|
||||
let confirmation = PermissionConfirmation {
|
||||
principal_type: PrincipalType::Tool,
|
||||
permission: Permission::from(decision),
|
||||
};
|
||||
|
||||
let handled = provider
|
||||
.handle_permission_confirmation(&id, &confirmation)
|
||||
.await;
|
||||
assert!(handled);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let tool_status = if saw_tool {
|
||||
Some(if tool_error {
|
||||
ToolCallStatus::Failed
|
||||
} else {
|
||||
ToolCallStatus::Completed
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
TestOutput { text, tool_status }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Connection for ClientToProviderConnection {
|
||||
type Session = ClientToProviderSession;
|
||||
|
||||
async fn new(config: TestConnectionConfig, openai: OpenAiFixture) -> Self {
|
||||
let (data_root, temp_dir) = match config.data_root.as_os_str().is_empty() {
|
||||
true => {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
(temp_dir.path().to_path_buf(), Some(temp_dir))
|
||||
}
|
||||
false => (config.data_root.clone(), None),
|
||||
};
|
||||
|
||||
let goose_mode = config.goose_mode;
|
||||
let mcp_servers = config.mcp_servers;
|
||||
|
||||
let (transport, _handle, permission_manager) = spawn_acp_server_in_process(
|
||||
openai.uri(),
|
||||
&config.builtins,
|
||||
data_root.as_path(),
|
||||
goose_mode,
|
||||
config.provider_factory,
|
||||
)
|
||||
.await;
|
||||
|
||||
let provider_config = AcpProviderConfig {
|
||||
command: "unused".into(),
|
||||
args: vec![],
|
||||
env: vec![],
|
||||
env_remove: vec![],
|
||||
work_dir: data_root,
|
||||
mcp_servers,
|
||||
session_mode_id: None,
|
||||
permission_mapping: PermissionMapping::default(),
|
||||
};
|
||||
|
||||
let provider = AcpProvider::connect_with_transport(
|
||||
"acp-test".to_string(),
|
||||
ModelConfig::new(TEST_MODEL).unwrap(),
|
||||
goose_mode,
|
||||
provider_config,
|
||||
transport.incoming,
|
||||
transport.outgoing,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let auth_methods = provider.auth_methods().to_vec();
|
||||
|
||||
Self {
|
||||
provider: Arc::new(Mutex::new(provider)),
|
||||
permission_manager,
|
||||
auth_methods,
|
||||
session_counter: 0,
|
||||
_openai: openai,
|
||||
_temp_dir: temp_dir,
|
||||
}
|
||||
}
|
||||
|
||||
async fn new_session(&mut self) -> (ClientToProviderSession, Option<SessionModelState>) {
|
||||
// Tests like run_model_set call new_session() multiple times on the same
|
||||
// connection, so each needs a distinct key to avoid returning a cached session.
|
||||
self.session_counter += 1;
|
||||
let goose_id = format!("test-session-{}", self.session_counter);
|
||||
let response = self
|
||||
.provider
|
||||
.lock()
|
||||
.await
|
||||
.ensure_session(Some(&goose_id))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let session = ClientToProviderSession {
|
||||
provider: Arc::clone(&self.provider),
|
||||
acp_session_id: response.session_id,
|
||||
session_id: goose_id,
|
||||
};
|
||||
(session, response.models)
|
||||
}
|
||||
|
||||
async fn load_session(
|
||||
&mut self,
|
||||
_session_id: &str,
|
||||
) -> (ClientToProviderSession, Option<SessionModelState>) {
|
||||
unimplemented!("TODO: implement load_session in ACP provider")
|
||||
}
|
||||
|
||||
fn auth_methods(&self) -> &[AuthMethod] {
|
||||
&self.auth_methods
|
||||
}
|
||||
|
||||
fn reset_openai(&self) {
|
||||
self._openai.reset();
|
||||
}
|
||||
|
||||
fn reset_permissions(&self) {
|
||||
self.permission_manager.remove_extension("");
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Session for ClientToProviderSession {
|
||||
fn session_id(&self) -> &sacp::schema::SessionId {
|
||||
&self.acp_session_id
|
||||
}
|
||||
|
||||
async fn prompt(&mut self, prompt: &str, decision: PermissionDecision) -> TestOutput {
|
||||
self.send_message(Message::user().with_text(prompt), decision)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn prompt_with_image(
|
||||
&mut self,
|
||||
prompt: &str,
|
||||
image_b64: &str,
|
||||
mime_type: &str,
|
||||
decision: PermissionDecision,
|
||||
) -> TestOutput {
|
||||
let message = Message::user()
|
||||
.with_image(image_b64, mime_type)
|
||||
.with_text(prompt);
|
||||
self.send_message(message, decision).await
|
||||
}
|
||||
|
||||
async fn set_model(&self, model_id: &str) {
|
||||
self.provider
|
||||
.lock()
|
||||
.await
|
||||
.set_model(&self.acp_session_id, model_id)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
+71
-34
@@ -5,8 +5,8 @@ use super::{
|
||||
use async_trait::async_trait;
|
||||
use goose::config::PermissionManager;
|
||||
use sacp::schema::{
|
||||
ContentBlock, InitializeRequest, LoadSessionRequest, McpServer, NewSessionRequest,
|
||||
PromptRequest, ProtocolVersion, RequestPermissionRequest, SessionModelState,
|
||||
AuthMethod, ContentBlock, ImageContent, InitializeRequest, LoadSessionRequest, McpServer,
|
||||
NewSessionRequest, PromptRequest, ProtocolVersion, RequestPermissionRequest, SessionModelState,
|
||||
SessionNotification, SessionUpdate, StopReason, TextContent, ToolCallStatus,
|
||||
};
|
||||
use sacp::{ClientToAgent, JrConnectionCx};
|
||||
@@ -22,6 +22,7 @@ pub struct ClientToAgentConnection {
|
||||
permission: Arc<Mutex<PermissionDecision>>,
|
||||
notify: Arc<Notify>,
|
||||
permission_manager: Arc<PermissionManager>,
|
||||
auth_methods: Vec<AuthMethod>,
|
||||
_openai: super::OpenAiFixture,
|
||||
_temp_dir: Option<tempfile::TempDir>,
|
||||
}
|
||||
@@ -34,6 +35,42 @@ pub struct ClientToAgentSession {
|
||||
notify: Arc<Notify>,
|
||||
}
|
||||
|
||||
impl ClientToAgentSession {
|
||||
async fn send_prompt(
|
||||
&mut self,
|
||||
content: Vec<ContentBlock>,
|
||||
decision: PermissionDecision,
|
||||
) -> TestOutput {
|
||||
*self.permission.lock().unwrap() = decision;
|
||||
self.updates.lock().unwrap().clear();
|
||||
|
||||
let response = self
|
||||
.cx
|
||||
.send_request(PromptRequest::new(self.session_id.clone(), content))
|
||||
.block_task()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(response.stop_reason, StopReason::EndTurn);
|
||||
|
||||
let mut updates_len = self.updates.lock().unwrap().len();
|
||||
while updates_len == 0 {
|
||||
self.notify.notified().await;
|
||||
updates_len = self.updates.lock().unwrap().len();
|
||||
}
|
||||
|
||||
let text = collect_agent_text(&self.updates);
|
||||
let deadline = tokio::time::Instant::now() + Duration::from_millis(500);
|
||||
let mut tool_status = extract_tool_status(&self.updates);
|
||||
while tool_status.is_none() && tokio::time::Instant::now() < deadline {
|
||||
tokio::task::yield_now().await;
|
||||
tool_status = extract_tool_status(&self.updates);
|
||||
}
|
||||
|
||||
TestOutput { text, tool_status }
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientToAgentConnection {
|
||||
#[allow(dead_code)]
|
||||
pub fn cx(&self) -> &JrConnectionCx<ClientToAgent> {
|
||||
@@ -67,7 +104,7 @@ impl Connection for ClientToAgentConnection {
|
||||
let notify = Arc::new(Notify::new());
|
||||
let permission = Arc::new(Mutex::new(PermissionDecision::Cancel));
|
||||
|
||||
let cx = {
|
||||
let (cx, auth_methods) = {
|
||||
let updates_clone = updates.clone();
|
||||
let notify_clone = notify.clone();
|
||||
let permission_clone = permission.clone();
|
||||
@@ -75,11 +112,13 @@ impl Connection for ClientToAgentConnection {
|
||||
let cx_holder: Arc<Mutex<Option<JrConnectionCx<ClientToAgent>>>> =
|
||||
Arc::new(Mutex::new(None));
|
||||
let cx_holder_clone = cx_holder.clone();
|
||||
let auth_holder: Arc<Mutex<Vec<AuthMethod>>> = Arc::new(Mutex::new(Vec::new()));
|
||||
let auth_holder_clone = auth_holder.clone();
|
||||
|
||||
let (ready_tx, ready_rx) = tokio::sync::oneshot::channel();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let permission_mapping = PermissionMapping;
|
||||
let permission_mapping = PermissionMapping::default();
|
||||
|
||||
let result = ClientToAgent::builder()
|
||||
.on_receive_notification(
|
||||
@@ -112,12 +151,15 @@ impl Connection for ClientToAgentConnection {
|
||||
.unwrap()
|
||||
.run_until({
|
||||
let cx_holder = cx_holder_clone;
|
||||
let auth_holder = auth_holder_clone;
|
||||
move |cx: JrConnectionCx<ClientToAgent>| async move {
|
||||
cx.send_request(InitializeRequest::new(ProtocolVersion::LATEST))
|
||||
let resp = cx
|
||||
.send_request(InitializeRequest::new(ProtocolVersion::LATEST))
|
||||
.block_task()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
*auth_holder.lock().unwrap() = resp.auth_methods;
|
||||
*cx_holder.lock().unwrap() = Some(cx.clone());
|
||||
let _ = ready_tx.send(());
|
||||
|
||||
@@ -133,7 +175,8 @@ impl Connection for ClientToAgentConnection {
|
||||
|
||||
ready_rx.await.unwrap();
|
||||
let cx = cx_holder.lock().unwrap().take().unwrap();
|
||||
cx
|
||||
let auth = std::mem::take(&mut *auth_holder.lock().unwrap());
|
||||
(cx, auth)
|
||||
};
|
||||
|
||||
Self {
|
||||
@@ -143,6 +186,7 @@ impl Connection for ClientToAgentConnection {
|
||||
permission,
|
||||
notify,
|
||||
permission_manager,
|
||||
auth_methods,
|
||||
_openai: openai,
|
||||
_temp_dir: temp_dir,
|
||||
}
|
||||
@@ -190,6 +234,10 @@ impl Connection for ClientToAgentConnection {
|
||||
(session, response.models)
|
||||
}
|
||||
|
||||
fn auth_methods(&self) -> &[AuthMethod] {
|
||||
&self.auth_methods
|
||||
}
|
||||
|
||||
fn reset_openai(&self) {
|
||||
self._openai.reset();
|
||||
}
|
||||
@@ -206,36 +254,25 @@ impl Session for ClientToAgentSession {
|
||||
}
|
||||
|
||||
async fn prompt(&mut self, text: &str, decision: PermissionDecision) -> TestOutput {
|
||||
*self.permission.lock().unwrap() = decision;
|
||||
self.updates.lock().unwrap().clear();
|
||||
|
||||
let response = self
|
||||
.cx
|
||||
.send_request(PromptRequest::new(
|
||||
self.session_id.clone(),
|
||||
vec![ContentBlock::Text(TextContent::new(text))],
|
||||
))
|
||||
.block_task()
|
||||
self.send_prompt(vec![ContentBlock::Text(TextContent::new(text))], decision)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(response.stop_reason, StopReason::EndTurn);
|
||||
|
||||
let mut updates_len = self.updates.lock().unwrap().len();
|
||||
while updates_len == 0 {
|
||||
self.notify.notified().await;
|
||||
updates_len = self.updates.lock().unwrap().len();
|
||||
}
|
||||
|
||||
let text = collect_agent_text(&self.updates);
|
||||
let deadline = tokio::time::Instant::now() + Duration::from_millis(500);
|
||||
let mut tool_status = extract_tool_status(&self.updates);
|
||||
while tool_status.is_none() && tokio::time::Instant::now() < deadline {
|
||||
tokio::task::yield_now().await;
|
||||
tool_status = extract_tool_status(&self.updates);
|
||||
}
|
||||
|
||||
TestOutput { text, tool_status }
|
||||
async fn prompt_with_image(
|
||||
&mut self,
|
||||
text: &str,
|
||||
image_b64: &str,
|
||||
mime_type: &str,
|
||||
decision: PermissionDecision,
|
||||
) -> TestOutput {
|
||||
self.send_prompt(
|
||||
vec![
|
||||
ContentBlock::Image(ImageContent::new(image_b64, mime_type)),
|
||||
ContentBlock::Text(TextContent::new(text)),
|
||||
],
|
||||
decision,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
// HACK: sacp doesn't support session/set_model yet, so we send it as untyped JSON.
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
mod common_tests;
|
||||
use common_tests::fixtures::provider::ClientToProviderConnection;
|
||||
use common_tests::fixtures::run_test;
|
||||
use common_tests::{
|
||||
run_config_mcp, run_initialize_doesnt_hit_provider, run_load_model, run_model_list,
|
||||
run_model_set, run_permission_persistence, run_prompt_basic, run_prompt_codemode,
|
||||
run_prompt_image, run_prompt_image_attachment, run_prompt_mcp,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_provider_config_mcp() {
|
||||
run_test(async { run_config_mcp::<ClientToProviderConnection>().await });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_provider_initialize_doesnt_hit_provider() {
|
||||
run_test(async { run_initialize_doesnt_hit_provider::<ClientToProviderConnection>().await });
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "TODO: implement load_session in ACP provider"]
|
||||
fn test_provider_load_model() {
|
||||
run_test(async { run_load_model::<ClientToProviderConnection>().await });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_provider_model_list() {
|
||||
run_test(async { run_model_list::<ClientToProviderConnection>().await });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_provider_model_set() {
|
||||
run_test(async { run_model_set::<ClientToProviderConnection>().await });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_provider_permission_persistence() {
|
||||
run_test(async { run_permission_persistence::<ClientToProviderConnection>().await });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_provider_prompt_basic() {
|
||||
run_test(async { run_prompt_basic::<ClientToProviderConnection>().await });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_provider_prompt_codemode() {
|
||||
run_test(async { run_prompt_codemode::<ClientToProviderConnection>().await });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_provider_prompt_image() {
|
||||
run_test(async { run_prompt_image::<ClientToProviderConnection>().await });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_provider_prompt_image_attachment() {
|
||||
run_test(async { run_prompt_image_attachment::<ClientToProviderConnection>().await });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_provider_prompt_mcp() {
|
||||
run_test(async { run_prompt_mcp::<ClientToProviderConnection>().await });
|
||||
}
|
||||
@@ -2,9 +2,9 @@ mod common_tests;
|
||||
use common_tests::fixtures::run_test;
|
||||
use common_tests::fixtures::server::ClientToAgentConnection;
|
||||
use common_tests::{
|
||||
run_config_mcp, run_initialize_without_provider, run_load_model, run_model_list, run_model_set,
|
||||
run_permission_persistence, run_prompt_basic, run_prompt_codemode, run_prompt_image,
|
||||
run_prompt_mcp,
|
||||
run_config_mcp, run_initialize_doesnt_hit_provider, run_load_model, run_model_list,
|
||||
run_model_set, run_permission_persistence, run_prompt_basic, run_prompt_codemode,
|
||||
run_prompt_image, run_prompt_image_attachment, run_prompt_mcp,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -13,8 +13,8 @@ fn test_config_mcp() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_initialize_without_provider() {
|
||||
run_test(async { run_initialize_without_provider().await });
|
||||
fn test_initialize_doesnt_hit_provider() {
|
||||
run_test(async { run_initialize_doesnt_hit_provider::<ClientToAgentConnection>().await });
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -52,6 +52,11 @@ fn test_prompt_image() {
|
||||
run_test(async { run_prompt_image::<ClientToAgentConnection>().await });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prompt_image_attachment() {
|
||||
run_test(async { run_prompt_image_attachment::<ClientToAgentConnection>().await });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prompt_mcp() {
|
||||
run_test(async { run_prompt_mcp::<ClientToAgentConnection>().await });
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"7E7t8"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Here"},"finish_reason":null}],"usage":null,"obfuscation":"C7q"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"’s"},"finish_reason":null}],"usage":null,"obfuscation":"2YLin"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" what"},"finish_reason":null}],"usage":null,"obfuscation":"X0"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" I"},"finish_reason":null}],"usage":null,"obfuscation":"cWHjE"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" can"},"finish_reason":null}],"usage":null,"obfuscation":"sSB"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" derive"},"finish_reason":null}],"usage":null,"obfuscation":""}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" from"},"finish_reason":null}],"usage":null,"obfuscation":"wh"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" the"},"finish_reason":null}],"usage":null,"obfuscation":"pdc"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" image"},"finish_reason":null}],"usage":null,"obfuscation":"6"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":":\n\n"},"finish_reason":null}],"usage":null,"obfuscation":"sf"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"8yQy1S"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" OCR"},"finish_reason":null}],"usage":null,"obfuscation":"8A1"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/text"},"finish_reason":null}],"usage":null,"obfuscation":"9R"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" transcription"},"finish_reason":null}],"usage":null,"obfuscation":"Ehc21BJqv"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":":\n"},"finish_reason":null}],"usage":null,"obfuscation":"9ab4"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" "},"finish_reason":null}],"usage":null,"obfuscation":"u2EwQi"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Hello"},"finish_reason":null}],"usage":null,"obfuscation":"5"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Goose"},"finish_reason":null}],"usage":null,"obfuscation":"b"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"!\n"},"finish_reason":null}],"usage":null,"obfuscation":"EsVy"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" "},"finish_reason":null}],"usage":null,"obfuscation":"Va8f84"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" This"},"finish_reason":null}],"usage":null,"obfuscation":"IR"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" is"},"finish_reason":null}],"usage":null,"obfuscation":"eaRL"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" a"},"finish_reason":null}],"usage":null,"obfuscation":"cdTOW"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" test"},"finish_reason":null}],"usage":null,"obfuscation":"WN"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" image"},"finish_reason":null}],"usage":null,"obfuscation":"l"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".\n\n"},"finish_reason":null}],"usage":null,"obfuscation":"lx"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"ABF429"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Observ"},"finish_reason":null}],"usage":null,"obfuscation":""}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"ations"},"finish_reason":null}],"usage":null,"obfuscation":"J"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":":\n"},"finish_reason":null}],"usage":null,"obfuscation":"iWhB"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" "},"finish_reason":null}],"usage":null,"obfuscation":"IFJGlV"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" -"},"finish_reason":null}],"usage":null,"obfuscation":"C8bxY"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" The"},"finish_reason":null}],"usage":null,"obfuscation":"UNZ"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" text"},"finish_reason":null}],"usage":null,"obfuscation":"aO"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" appears"},"finish_reason":null}],"usage":null,"obfuscation":"fMW5Ik2fIuCxYIi"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" in"},"finish_reason":null}],"usage":null,"obfuscation":"6Csr"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" two"},"finish_reason":null}],"usage":null,"obfuscation":"1OA"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" lines"},"finish_reason":null}],"usage":null,"obfuscation":"6"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" on"},"finish_reason":null}],"usage":null,"obfuscation":"MfK8"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" a"},"finish_reason":null}],"usage":null,"obfuscation":"Xn4NP"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" white"},"finish_reason":null}],"usage":null,"obfuscation":"Z"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" background"},"finish_reason":null}],"usage":null,"obfuscation":"xJbYCha0CxG5"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" with"},"finish_reason":null}],"usage":null,"obfuscation":"pN"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" black"},"finish_reason":null}],"usage":null,"obfuscation":"7"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" sans"},"finish_reason":null}],"usage":null,"obfuscation":"ni"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-serif"},"finish_reason":null}],"usage":null,"obfuscation":"X"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" style"},"finish_reason":null}],"usage":null,"obfuscation":"W"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".\n"},"finish_reason":null}],"usage":null,"obfuscation":"TPOZ"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" "},"finish_reason":null}],"usage":null,"obfuscation":"SpMYza"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" -"},"finish_reason":null}],"usage":null,"obfuscation":"CSuHP"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Lik"},"finish_reason":null}],"usage":null,"obfuscation":"Wgi"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"ely"},"finish_reason":null}],"usage":null,"obfuscation":"dhDR"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" centered"},"finish_reason":null}],"usage":null,"obfuscation":"ai50dSkUIc0bS6"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" alignment"},"finish_reason":null}],"usage":null,"obfuscation":"8sTNdzGIIYmdW"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"finish_reason":null}],"usage":null,"obfuscation":"Fby3dv"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" two"},"finish_reason":null}],"usage":null,"obfuscation":"Lpl"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-line"},"finish_reason":null}],"usage":null,"obfuscation":"xf"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" layout"},"finish_reason":null}],"usage":null,"obfuscation":""}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".\n\n"},"finish_reason":null}],"usage":null,"obfuscation":"CM"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"What"},"finish_reason":null}],"usage":null,"obfuscation":"zy9"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" would"},"finish_reason":null}],"usage":null,"obfuscation":"d"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" you"},"finish_reason":null}],"usage":null,"obfuscation":"qyo"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" like"},"finish_reason":null}],"usage":null,"obfuscation":"tG"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" me"},"finish_reason":null}],"usage":null,"obfuscation":"KJ3J"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" to"},"finish_reason":null}],"usage":null,"obfuscation":"jTJN"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" do"},"finish_reason":null}],"usage":null,"obfuscation":"Kvuu"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" next"},"finish_reason":null}],"usage":null,"obfuscation":"Q4"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"?\n"},"finish_reason":null}],"usage":null,"obfuscation":"MKya"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"FGV2M3"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Translate"},"finish_reason":null}],"usage":null,"obfuscation":"ojudhthRN0wje"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" the"},"finish_reason":null}],"usage":null,"obfuscation":"j1d"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" text"},"finish_reason":null}],"usage":null,"obfuscation":"IN"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n"},"finish_reason":null}],"usage":null,"obfuscation":"5GPdt"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"GdygSH"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Extract"},"finish_reason":null}],"usage":null,"obfuscation":"CocHxzrTqsaRFbq"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" more"},"finish_reason":null}],"usage":null,"obfuscation":"LN"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" metadata"},"finish_reason":null}],"usage":null,"obfuscation":"52g08uQUy8fzv2"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" ("},"finish_reason":null}],"usage":null,"obfuscation":"AmD59"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"font"},"finish_reason":null}],"usage":null,"obfuscation":"Qvr"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"finish_reason":null}],"usage":null,"obfuscation":"DKTZR4"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" size"},"finish_reason":null}],"usage":null,"obfuscation":"sa"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":","},"finish_reason":null}],"usage":null,"obfuscation":"kQknkh"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" contrast"},"finish_reason":null}],"usage":null,"obfuscation":"CzLtt2a3ZKZRsk"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":")\n"},"finish_reason":null}],"usage":null,"obfuscation":"10ts"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"BkGfhE"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Generate"},"finish_reason":null}],"usage":null,"obfuscation":"kRp8kkudiLXyPr"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" alt"},"finish_reason":null}],"usage":null,"obfuscation":"naE"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" text"},"finish_reason":null}],"usage":null,"obfuscation":"WO"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" or"},"finish_reason":null}],"usage":null,"obfuscation":"wjCo"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" accessibility"},"finish_reason":null}],"usage":null,"obfuscation":"HgWk0MkQP"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" description"},"finish_reason":null}],"usage":null,"obfuscation":"2RkUmSr6mK5"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n"},"finish_reason":null}],"usage":null,"obfuscation":"qCmgA"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"dDYudm"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Create"},"finish_reason":null}],"usage":null,"obfuscation":""}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" a"},"finish_reason":null}],"usage":null,"obfuscation":"Uco4R"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" caption"},"finish_reason":null}],"usage":null,"obfuscation":"cAPhTd1DoHVetWW"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" or"},"finish_reason":null}],"usage":null,"obfuscation":"qK8N"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" use"},"finish_reason":null}],"usage":null,"obfuscation":"wGO"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" it"},"finish_reason":null}],"usage":null,"obfuscation":"DKP3"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" in"},"finish_reason":null}],"usage":null,"obfuscation":"0qux"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" code"},"finish_reason":null}],"usage":null,"obfuscation":"FI"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" ("},"finish_reason":null}],"usage":null,"obfuscation":"BXnPm"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"HTML"},"finish_reason":null}],"usage":null,"obfuscation":"lal"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/C"},"finish_reason":null}],"usage":null,"obfuscation":"2bBrS"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"SS"},"finish_reason":null}],"usage":null,"obfuscation":"kM3Jx"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":")\n"},"finish_reason":null}],"usage":null,"obfuscation":"H7Et"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"EbOoyr"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" Run"},"finish_reason":null}],"usage":null,"obfuscation":"h74"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" OCR"},"finish_reason":null}],"usage":null,"obfuscation":"3vs"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" on"},"finish_reason":null}],"usage":null,"obfuscation":"N3y0"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" similar"},"finish_reason":null}],"usage":null,"obfuscation":"mMnr4ffD7zygIYv"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" images"},"finish_reason":null}],"usage":null,"obfuscation":""}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" for"},"finish_reason":null}],"usage":null,"obfuscation":"TJp"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" batch"},"finish_reason":null}],"usage":null,"obfuscation":"6"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" processing"},"finish_reason":null}],"usage":null,"obfuscation":"ls6HOZgAV1ez"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":null,"obfuscation":"j"}
|
||||
|
||||
data: {"id":"chatcmpl-DHk8aNO2gKKqlrPxXg3cHSzIBitLf","object":"chat.completion.chunk","created":1773121300,"model":"gpt-5-nano-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":2768,"completion_tokens":572,"total_tokens":3340,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":448,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"EvMBUZOQw2ps4DX"}
|
||||
|
||||
data: [DONE]
|
||||
|
||||
Reference in New Issue
Block a user