1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//! Real OpenAI Chat Completions provider (feature `openai`).
//!
//! This is one of the concrete leaves the recursive runtime bottoms out in: a
//! single [`OpenAiModel`] backs hosted OpenAI *and* every OpenAI-compatible
//! endpoint (Anthropic, Ollama, DeepSeek, Groq, xAI, OpenRouter, Together,
//! Mistral) via the preset constructors below, so the sub-agent / sub-graph
//! layers above never need to know which provider answered.
//!
//! [`OpenAiModel`] implements [`ChatModel`] against the hosted OpenAI Chat
//! Completions endpoint (`POST {base_url}/chat/completions`). It translates the
//! provider-neutral [`ModelRequest`] into OpenAI's JSON wire format (see
//! [`types`]), performs the HTTP call with `reqwest`, and maps the response back
//! into a [`ModelResponse`] with a fully-populated [`AssistantMessage`],
//! [`ToolCall`]s, [`Usage`], and finish reason.
//!
//! The wire (de)serialization shapes live in [`types`]; this module owns only
//! the translation logic and the HTTP transport, keeping OpenAI-specific JSON
//! out of the rest of the harness.
//!
//! Local OpenAI-compatible runtimes (LM Studio, llama.cpp server, …) reject a
//! named `tool_choice` object and a `json_object` response format with an HTTP
//! 400. The transport degrades both to shapes they accept — `tool_choice`
//! `"required"` with the `tools` array filtered to the named tool, and a
//! permissive `json_schema` — either eagerly via
//! [`OpenAiModel::with_named_tool_choice`] / [`OpenAiModel::with_json_object_format`]
//! or automatically as a single retry when a 400 body implicates the shape. See
//! the module `README.md` "Local-server compatibility" section.
//!
//! # Example
//!
//! ```no_run
//! use tinyagents::harness::providers::openai::OpenAiModel;
//!
//! # fn main() -> tinyagents::Result<()> {
//! // Reads OPENAI_API_KEY (and optional OPENAI_MODEL / OPENAI_BASE_URL).
//! let model = OpenAiModel::from_env()?;
//! # let _ = model;
//! # Ok(())
//! # }
//! ```
pub use *;
use VecDeque;
use Pin;
use Duration;
use async_trait;
use ;
use ;
use crate;
use crate;
use crate;
use crate;
use crateUsage;
use ProviderSpec;
/// Default model id used when neither the request nor the builder override it.
const DEFAULT_MODEL: &str = "gpt-4.1-mini";
/// Default OpenAI API base URL.
const DEFAULT_BASE_URL: &str = "https://api.openai.com/v1";
/// Sane default TCP connect timeout applied to every call. Bounds connection
/// establishment without capping the (potentially long) response body, so it is
/// safe for streaming too.
const DEFAULT_CONNECT_TIMEOUT_SECS: u64 = 30;
/// Default overall timeout applied to unary calls when the request does not set
/// [`ModelRequest::timeout_ms`]. Streaming calls get no overall cap by default.
const DEFAULT_REQUEST_TIMEOUT_SECS: u64 = 600;
pub use ReasoningTagExtraction;
pub use ;
use *;
use *;
use *;
use ;