-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathModelBuilder.kt
More file actions
181 lines (167 loc) · 7.35 KB
/
Copy pathModelBuilder.kt
File metadata and controls
181 lines (167 loc) · 7.35 KB
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package agents_engine.model
import java.net.http.HttpClient
import kotlin.time.Duration
class ModelBuilder {
var name: String = ""
var provider: ModelProvider = ModelProvider.OLLAMA
var temperature: Double = 0.7
var host: String = "localhost"
var port: Int = 11434
var client: ModelClient? = null
var apiKey: String? = null
var anthropicBaseUrl: String = "https://api.anthropic.com"
var openAiBaseUrl: String = "https://api.openai.com"
var deepSeekBaseUrl: String = DeepSeekClient.DEFAULT_BASE_URL
var kimiBaseUrl: String = KimiClient.DEFAULT_BASE_URL
var openRouterBaseUrl: String = OpenRouterClient.DEFAULT_BASE_URL
var perplexityBaseUrl: String = PerplexityClient.DEFAULT_BASE_URL
var geminiBaseUrl: String = GeminiClient.DEFAULT_BASE_URL
var openRouterHttpReferer: String? = null
var openRouterXTitle: String? = null
var maxTokens: Int = ClaudeClient.DEFAULT_MAX_TOKENS
/**
* #2850 — override the adapter's [DEFAULT_REQUEST_TIMEOUT]. Null
* (default) → use the adapter's 300s floor. Bump this when long
* Sonnet turns / big Ollama generations / extended-thinking calls
* regularly exceed the default; the JDK HttpClient aborts the call
* at this cap and the framework surfaces it as
* `HttpTimeoutException`.
*/
var requestTimeout: Duration? = null
/**
* #2850 — override the adapter's [DEFAULT_CONNECT_TIMEOUT]. Null
* (default) → 10s, which is right for every healthy network. Tune
* for cross-region or slow-proxy deployments where TCP connect
* itself is the bottleneck.
*/
var connectTimeout: Duration? = null
/** #2385 — opt into a shared `HttpClient` across agents (pool / proxy / rate-limit). */
var httpClient: HttpClient? = null
fun ollama(modelName: String) {
name = modelName
provider = ModelProvider.OLLAMA
}
/**
* Select Anthropic Claude (#1644). [ClaudeClient] is constructed lazily at
* AgenticLoop time so the agent's full tool catalog is available — same
* pattern [OllamaClient] uses.
*/
fun claude(modelName: String) {
name = modelName
provider = ModelProvider.ANTHROPIC
}
/**
* Select OpenAI Chat Completions (#1656). [OpenAiClient] is constructed
* lazily at AgenticLoop time so the agent's full tool catalog is available.
*/
fun openai(modelName: String) {
name = modelName
provider = ModelProvider.OPENAI
}
/**
* Select DeepSeek Chat Completions. [DeepSeekClient] is constructed lazily
* at AgenticLoop time so the agent's full tool catalog is available.
*/
fun deepseek(modelName: String) {
name = modelName
provider = ModelProvider.DEEPSEEK
}
/**
* Select Kimi (Moonshot AI) Chat Completions (#2697). [KimiClient] is
* constructed lazily at AgenticLoop time so the agent's full tool catalog
* is available. Model names follow Moonshot's naming, e.g.
* `"moonshot-v1-8k"`, `"moonshot-v1-32k"`, `"moonshot-v1-128k"`.
*/
fun kimi(modelName: String, region: KimiRegion? = null) {
name = modelName
provider = ModelProvider.KIMI
// #4883 — only override the base URL when a region is explicitly chosen, so existing
// `kimi("...")` calls (and any manual `kimiBaseUrl = ...`) keep the China default untouched.
if (region != null) kimiBaseUrl = region.baseUrl
}
/**
* Select OpenRouter — the multi-provider OpenAI-compatible aggregator (#2701).
* Model names follow OpenRouter's `provider/model` convention, e.g.
* `"anthropic/claude-3.5-sonnet"`, `"openai/gpt-4o-mini"`,
* `"meta-llama/llama-3.3-70b-instruct:free"` (free tier).
* [OpenRouterClient] is constructed lazily at AgenticLoop time so the
* agent's full tool catalog is available.
*/
fun openrouter(modelName: String) {
name = modelName
provider = ModelProvider.OPENROUTER
}
/**
* Select Perplexity (Sonar) Chat Completions (#3675). [PerplexityClient]
* is constructed lazily at AgenticLoop time so the agent's full tool
* catalog is available. Model ids follow Perplexity's `sonar` family, e.g.
* `"sonar"`, `"sonar-pro"`, `"sonar-reasoning-pro"`, `"sonar-deep-research"`.
* For web-grounded search from an agent on a different model, prefer the
* `perplexitySearch` tool (#3676) over selecting a sonar model here.
*/
fun perplexity(modelName: String) {
name = modelName
provider = ModelProvider.PERPLEXITY
}
/**
* Select Google Gemini (Generative Language API) (#1917). [GeminiClient] is constructed lazily
* at AgenticLoop time so the agent's full tool catalog is available. Model ids follow Google's
* naming, e.g. `"gemini-2.5-flash"`, `"gemini-2.5-pro"`, `"gemini-2.0-flash"`. Requires an API
* key from Google AI Studio (load from `.secrets/gemini-key`).
*/
fun gemini(modelName: String) {
name = modelName
provider = ModelProvider.GEMINI
}
/** Backing field for the [reasoning] DSL (#2406). Off by default. */
private var reasoningConfig: ReasoningConfig? = null
/**
* Enable reasoning/thinking for this model (#2406). Off unless called.
* `budgetTokens` feeds Claude's thinking budget; `effort` feeds OpenAI's
* `reasoning_effort`. Providers that don't expose reasoning ignore it.
*/
fun reasoning(
enabled: Boolean = true,
budgetTokens: Int? = null,
effort: ReasoningEffort? = null,
) {
reasoningConfig = ReasoningConfig(enabled, budgetTokens, effort)
}
internal fun build(): ModelConfig {
if (client == null && apiKey == null) {
when (provider) {
ModelProvider.ANTHROPIC -> error("model { claude(\"$name\") } requires apiKey to be set")
ModelProvider.OPENAI -> error("model { openai(\"$name\") } requires apiKey to be set")
ModelProvider.DEEPSEEK -> error("model { deepseek(\"$name\") } requires apiKey to be set")
ModelProvider.KIMI -> error("model { kimi(\"$name\") } requires apiKey to be set")
ModelProvider.OPENROUTER -> error("model { openrouter(\"$name\") } requires apiKey to be set")
ModelProvider.PERPLEXITY -> error("model { perplexity(\"$name\") } requires apiKey to be set")
ModelProvider.GEMINI -> error("model { gemini(\"$name\") } requires apiKey to be set")
ModelProvider.OLLAMA -> Unit
}
}
return ModelConfig(
name = name,
provider = provider,
temperature = temperature,
host = host,
port = port,
client = client,
apiKey = apiKey,
anthropicBaseUrl = anthropicBaseUrl,
openAiBaseUrl = openAiBaseUrl,
deepSeekBaseUrl = deepSeekBaseUrl,
kimiBaseUrl = kimiBaseUrl,
openRouterBaseUrl = openRouterBaseUrl,
perplexityBaseUrl = perplexityBaseUrl,
geminiBaseUrl = geminiBaseUrl,
openRouterHttpReferer = openRouterHttpReferer,
openRouterXTitle = openRouterXTitle,
maxTokens = maxTokens,
reasoning = reasoningConfig,
requestTimeout = requestTimeout,
connectTimeout = connectTimeout,
httpClient = httpClient,
)
}
}