laiya 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/providers-and-models.md +36 -0
- data/examples/chatgpt/readme.md +42 -26
- data/examples/chatgpt/service.rb +2 -2
- data/guides/providers-and-models/readme.md +36 -0
- data/lib/laiya/provider/codex.rb +128 -1
- data/lib/laiya/provider/ollama.rb +86 -0
- data/lib/laiya/version.rb +1 -1
- data/readme.md +6 -0
- data/releases.md +6 -0
- data.tar.gz.sig +0 -0
- metadata +1 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f1d79e801894376e7f3a398c48d22f0c5b0100760751dc8fecb63c044f665de4
|
|
4
|
+
data.tar.gz: 9a72dbf43fc8eba78543c3d8de3acdbd25072b0db940688f21ffa0fff5dc9b93
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cc9253bf7a64e2ca4cf8b6fbdf316db4a625af93c9232943c5faa25f6d726002ece63158f38d1acc967b5b5db5fd849a7cc9801d5f207c9f0cca529da5ef968d
|
|
7
|
+
data.tar.gz: be8830a0dee2d6f0fc1219035ccce4de588704706aae3c2f4b3c459d3556e234f629dec67283c84be52cdbf3e429055d6019d4e965fa1448f01e50504ea16ab3
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -73,6 +73,42 @@ token counts. OpenCode does not currently infer these custom fields from
|
|
|
73
73
|
`limit.output` values on each client as well. The `context` limit should match
|
|
74
74
|
the effective upstream model configuration (for Ollama, including `num_ctx`).
|
|
75
75
|
|
|
76
|
+
## Expose Ollama Thinking Controls
|
|
77
|
+
|
|
78
|
+
When Ollama model discovery is enabled, Laiya also queries `/api/show` for each
|
|
79
|
+
model and publishes available thinking controls under `laiya.reasoning`. Named
|
|
80
|
+
levels appear as `supported_efforts` with a `default_effort`; boolean-only
|
|
81
|
+
controls appear as `thinking_values` with a `default_thinking`. If a model has
|
|
82
|
+
no thinking metadata, advertises only `[false]`, or `/api/show` is unavailable,
|
|
83
|
+
Laiya still lists it without reasoning metadata.
|
|
84
|
+
|
|
85
|
+
The OpenAI-compatible proxy already forwards `reasoning_effort` requests to
|
|
86
|
+
Ollama. OpenCode custom providers do not automatically turn Laiya's extension
|
|
87
|
+
metadata into variants, so configure variants from the discovered values in
|
|
88
|
+
each OpenCode model entry:
|
|
89
|
+
|
|
90
|
+
```jsonc
|
|
91
|
+
{
|
|
92
|
+
"providers": {
|
|
93
|
+
"laiya": {
|
|
94
|
+
"models": {
|
|
95
|
+
"qwen3-coder": {
|
|
96
|
+
"modelID": "qwen3-coder:latest",
|
|
97
|
+
"variants": [
|
|
98
|
+
{"id": "low", "body": {"reasoning_effort": "low"}},
|
|
99
|
+
{"id": "medium", "body": {"reasoning_effort": "medium"}},
|
|
100
|
+
{"id": "high", "body": {"reasoning_effort": "high"}},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Only configure variants advertised by the selected model. Thinking metadata and
|
|
110
|
+
supported levels are model-specific.
|
|
111
|
+
|
|
76
112
|
## Customize Discovery
|
|
77
113
|
|
|
78
114
|
Subclass `Laiya::Models::Discover` to filter models or attach provider-specific
|
data/examples/chatgpt/readme.md
CHANGED
|
@@ -43,11 +43,13 @@ behind TLS and an access-controlled gateway. The service requires the caller to
|
|
|
43
43
|
send the `LAIYA_API_KEY` as a Bearer token; this is separate from the Codex
|
|
44
44
|
credentials used upstream.
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
The Codex provider discovers account-visible, API-supported models from the
|
|
47
|
+
authenticated Codex model catalog and refreshes the discovered list periodically.
|
|
48
|
+
The provider detects the installed Codex CLI version for the catalog request;
|
|
49
|
+
set `CODEX_CLIENT_VERSION` to override it if the CLI is unavailable on the
|
|
50
|
+
service's `PATH` or you need to use a different version. Set `CODEX_HOME` if the
|
|
51
|
+
credential file is in a non-default directory. `LAIYA_URL` can change the bind
|
|
52
|
+
endpoint; do not bind publicly without TLS and network access controls.
|
|
51
53
|
|
|
52
54
|
## Try it
|
|
53
55
|
|
|
@@ -79,29 +81,43 @@ available because the Codex backend is used statelessly.
|
|
|
79
81
|
## Connect OpenCode from another computer
|
|
80
82
|
|
|
81
83
|
Put the service behind TLS and an access-controlled network gateway, then add a
|
|
82
|
-
custom Responses-
|
|
83
|
-
the
|
|
84
|
+
custom Responses-compatible provider to OpenCode. Codex tool turns require
|
|
85
|
+
`/v1/responses`; the Chat Completions adapter intentionally rejects them. Keep
|
|
86
|
+
the Laiya API key in the client machine's secret configuration rather than
|
|
87
|
+
committing it:
|
|
84
88
|
|
|
85
|
-
```
|
|
89
|
+
```jsonc
|
|
86
90
|
{
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
91
|
+
"$schema": "https://opencode.ai/config.json",
|
|
92
|
+
"providers": {
|
|
93
|
+
"laiya-codex": {
|
|
94
|
+
"name": "Laiya Codex",
|
|
95
|
+
"env": ["LAIYA_API_KEY"],
|
|
96
|
+
"package": "@opencode/ai/providers/openai-compatible/responses",
|
|
97
|
+
"settings": {
|
|
98
|
+
"baseURL": "https://laiya.example.com/v1",
|
|
99
|
+
},
|
|
100
|
+
"models": {
|
|
101
|
+
"gpt-6-luna": {
|
|
102
|
+
"name": "GPT-6 Luna",
|
|
103
|
+
"variants": [
|
|
104
|
+
{"id": "low", "settings": {"reasoningEffort": "low"}},
|
|
105
|
+
{"id": "medium", "settings": {"reasoningEffort": "medium"}},
|
|
106
|
+
{"id": "high", "settings": {"reasoningEffort": "high"}},
|
|
107
|
+
{"id": "xhigh", "settings": {"reasoningEffort": "xhigh"}},
|
|
108
|
+
{"id": "max", "settings": {"reasoningEffort": "max"}},
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
"model": "laiya-codex/gpt-6-luna",
|
|
102
115
|
}
|
|
103
116
|
```
|
|
104
117
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
118
|
+
Codex entries returned by `GET /v1/models` include the supported reasoning
|
|
119
|
+
efforts in `laiya.reasoning.supported_efforts` and the default in
|
|
120
|
+
`laiya.reasoning.default_effort`. Use those values to configure OpenCode's
|
|
121
|
+
model variants; its custom-provider model list remains explicit. The model can
|
|
122
|
+
return tool calls, which OpenCode executes on the client computer and reports
|
|
123
|
+
back on subsequent requests; Laiya never executes client tools.
|
data/examples/chatgpt/service.rb
CHANGED
|
@@ -17,8 +17,8 @@ configuration = Async::Service::Configuration.build do
|
|
|
17
17
|
|
|
18
18
|
configuration do
|
|
19
19
|
Laiya::Configuration.build do |builder|
|
|
20
|
-
|
|
21
|
-
builder.
|
|
20
|
+
codex = Laiya::Provider::Codex.new
|
|
21
|
+
builder.provider :codex, codex, models: :discover
|
|
22
22
|
builder.default_provider :codex
|
|
23
23
|
end
|
|
24
24
|
end
|
|
@@ -73,6 +73,42 @@ token counts. OpenCode does not currently infer these custom fields from
|
|
|
73
73
|
`limit.output` values on each client as well. The `context` limit should match
|
|
74
74
|
the effective upstream model configuration (for Ollama, including `num_ctx`).
|
|
75
75
|
|
|
76
|
+
## Expose Ollama Thinking Controls
|
|
77
|
+
|
|
78
|
+
When Ollama model discovery is enabled, Laiya also queries `/api/show` for each
|
|
79
|
+
model and publishes available thinking controls under `laiya.reasoning`. Named
|
|
80
|
+
levels appear as `supported_efforts` with a `default_effort`; boolean-only
|
|
81
|
+
controls appear as `thinking_values` with a `default_thinking`. If a model has
|
|
82
|
+
no thinking metadata, advertises only `[false]`, or `/api/show` is unavailable,
|
|
83
|
+
Laiya still lists it without reasoning metadata.
|
|
84
|
+
|
|
85
|
+
The OpenAI-compatible proxy already forwards `reasoning_effort` requests to
|
|
86
|
+
Ollama. OpenCode custom providers do not automatically turn Laiya's extension
|
|
87
|
+
metadata into variants, so configure variants from the discovered values in
|
|
88
|
+
each OpenCode model entry:
|
|
89
|
+
|
|
90
|
+
```jsonc
|
|
91
|
+
{
|
|
92
|
+
"providers": {
|
|
93
|
+
"laiya": {
|
|
94
|
+
"models": {
|
|
95
|
+
"qwen3-coder": {
|
|
96
|
+
"modelID": "qwen3-coder:latest",
|
|
97
|
+
"variants": [
|
|
98
|
+
{"id": "low", "body": {"reasoning_effort": "low"}},
|
|
99
|
+
{"id": "medium", "body": {"reasoning_effort": "medium"}},
|
|
100
|
+
{"id": "high", "body": {"reasoning_effort": "high"}},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Only configure variants advertised by the selected model. Thinking metadata and
|
|
110
|
+
supported levels are model-specific.
|
|
111
|
+
|
|
76
112
|
## Customize Discovery
|
|
77
113
|
|
|
78
114
|
Subclass `Laiya::Models::Discover` to filter models or attach provider-specific
|
data/lib/laiya/provider/codex.rb
CHANGED
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
require "async/http"
|
|
7
7
|
require "json"
|
|
8
|
+
require "open3"
|
|
8
9
|
require "protocol/http/body/streamable"
|
|
9
10
|
require "securerandom"
|
|
11
|
+
require "uri"
|
|
10
12
|
|
|
11
13
|
require_relative "interface"
|
|
12
14
|
require_relative "codex/authentication"
|
|
@@ -26,20 +28,66 @@ module Laiya
|
|
|
26
28
|
DEFAULT_ENDPOINT = "https://chatgpt.com/backend-api/codex"
|
|
27
29
|
USER_AGENT = "laiya-codex/0.0.0"
|
|
28
30
|
|
|
31
|
+
# Return the configured or installed Codex CLI version.
|
|
32
|
+
# @returns [String | Nil] The Codex CLI version, or `nil` when it cannot be detected.
|
|
33
|
+
def self.client_version
|
|
34
|
+
if version = ENV["CODEX_CLIENT_VERSION"]
|
|
35
|
+
return version unless version.empty?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
output, status = Open3.capture2("codex", "--version")
|
|
39
|
+
return unless status.success?
|
|
40
|
+
|
|
41
|
+
return output[/\bcodex(?:-cli)?\s+(\S+)/, 1]
|
|
42
|
+
rescue Errno::ENOENT
|
|
43
|
+
nil
|
|
44
|
+
end
|
|
45
|
+
|
|
29
46
|
# Initialize the experimental ChatGPT Codex API adapter.
|
|
30
47
|
# @option :authentication [Interface(:credentials) | Nil] A credential source.
|
|
31
48
|
# @option :codex_home [String] The Codex home directory containing `auth.json`.
|
|
49
|
+
# @option :client_version [String | Nil] The Codex CLI version used for model discovery; defaults to `CODEX_CLIENT_VERSION` or `codex --version`.
|
|
32
50
|
# @option :endpoint [String | Async::HTTP::Endpoint] The Codex backend endpoint.
|
|
33
51
|
# @option :client [Interface(:call) | Nil] An optional HTTP client.
|
|
34
|
-
def initialize(authentication: nil, codex_home: ENV.fetch("CODEX_HOME", Authentication::DEFAULT_CODEX_HOME), endpoint: DEFAULT_ENDPOINT, client: nil, **client_options)
|
|
52
|
+
def initialize(authentication: nil, codex_home: ENV.fetch("CODEX_HOME", Authentication::DEFAULT_CODEX_HOME), client_version: ENV["CODEX_CLIENT_VERSION"], endpoint: DEFAULT_ENDPOINT, client: nil, **client_options)
|
|
35
53
|
@endpoint = Async::HTTP::Endpoint[endpoint]
|
|
36
54
|
@authentication = authentication || Authentication.new(codex_home: codex_home)
|
|
55
|
+
@client_version = client_version
|
|
56
|
+
@client_version_detected = client_version && !client_version.empty?
|
|
37
57
|
@client = client || Async::HTTP::Client.new(@endpoint, **client_options)
|
|
38
58
|
@owns_client = client.nil?
|
|
39
59
|
end
|
|
40
60
|
|
|
41
61
|
attr :endpoint
|
|
42
62
|
|
|
63
|
+
# Fetch and normalize the authenticated Codex model catalog.
|
|
64
|
+
# @returns [Protocol::HTTP::Response] The OpenAI-compatible model list.
|
|
65
|
+
def models
|
|
66
|
+
client_version = self.client_version
|
|
67
|
+
unless client_version
|
|
68
|
+
return error_response(500, "Set CODEX_CLIENT_VERSION or install the Codex CLI to discover models", "server_error")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
credentials = @authentication.credentials
|
|
72
|
+
upstream = request_models(credentials, client_version)
|
|
73
|
+
|
|
74
|
+
if upstream.status == 401
|
|
75
|
+
upstream.close
|
|
76
|
+
credentials = @authentication.credentials(refresh: true)
|
|
77
|
+
upstream = request_models(credentials, client_version)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
unless upstream.status >= 200 && upstream.status < 300
|
|
81
|
+
return upstream
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
model_catalog_response(upstream)
|
|
85
|
+
rescue Authentication::Error
|
|
86
|
+
error_response(502, "Codex authentication failed", "server_error")
|
|
87
|
+
rescue StandardError
|
|
88
|
+
error_response(502, "Codex model discovery failed", "server_error")
|
|
89
|
+
end
|
|
90
|
+
|
|
43
91
|
# Convert supported Chat Completions requests or proxy Responses requests.
|
|
44
92
|
# @parameter request [Protocol::HTTP::Request] The incoming OpenAI-compatible request.
|
|
45
93
|
# @returns [Protocol::HTTP::Response] The translated or proxied response.
|
|
@@ -107,6 +155,85 @@ module Laiya
|
|
|
107
155
|
|
|
108
156
|
private
|
|
109
157
|
|
|
158
|
+
def client_version
|
|
159
|
+
return @client_version if @client_version_detected
|
|
160
|
+
|
|
161
|
+
@client_version_detected = true
|
|
162
|
+
@client_version = self.class.client_version
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def request_models(credentials, client_version)
|
|
166
|
+
path = "#{@endpoint.path.split("?", 2).first.sub(/\/+\z/, "")}/models?#{URI.encode_www_form(client_version: client_version)}"
|
|
167
|
+
|
|
168
|
+
headers = Protocol::HTTP::Headers[
|
|
169
|
+
"accept" => "application/json",
|
|
170
|
+
"authorization" => "Bearer #{credentials.fetch(:access_token)}",
|
|
171
|
+
"originator" => "laiya",
|
|
172
|
+
"user-agent" => USER_AGENT,
|
|
173
|
+
]
|
|
174
|
+
|
|
175
|
+
if account_id = credentials[:account_id]
|
|
176
|
+
headers["chatgpt-account-id"] = account_id
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
if residency = credentials[:residency]
|
|
180
|
+
headers["x-openai-internal-codex-residency"] = residency
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
return @client.call(Protocol::HTTP::Request["GET", path, headers])
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def model_catalog_response(upstream)
|
|
187
|
+
payload = JSON.parse(upstream.read)
|
|
188
|
+
models = payload.fetch("models")
|
|
189
|
+
unless models.is_a?(Array)
|
|
190
|
+
return error_response(502, "Codex model catalog was invalid", "server_error")
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
data = models.filter_map{|model| normalize_model(model)}
|
|
194
|
+
return Protocol::HTTP::Response[
|
|
195
|
+
200,
|
|
196
|
+
{"content-type" => "application/json"},
|
|
197
|
+
[JSON.dump(object: "list", data: data)],
|
|
198
|
+
]
|
|
199
|
+
rescue JSON::ParserError, KeyError, TypeError
|
|
200
|
+
error_response(502, "Codex model catalog was invalid", "server_error")
|
|
201
|
+
ensure
|
|
202
|
+
upstream.close
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def normalize_model(model)
|
|
206
|
+
return unless model.is_a?(Hash)
|
|
207
|
+
return unless model["supported_in_api"] == true && model["visibility"] == "list"
|
|
208
|
+
|
|
209
|
+
id = model["slug"]
|
|
210
|
+
return unless id.is_a?(String) && !id.empty?
|
|
211
|
+
|
|
212
|
+
metadata = {}
|
|
213
|
+
metadata["name"] = model["display_name"] if model["display_name"].is_a?(String)
|
|
214
|
+
if (context = model["context_window"]).is_a?(Integer) && context.positive?
|
|
215
|
+
metadata["limits"] = {"context" => context}
|
|
216
|
+
end
|
|
217
|
+
efforts = Array(model["supported_reasoning_levels"]).filter_map do |level|
|
|
218
|
+
level["effort"] if level.is_a?(Hash) && level["effort"].is_a?(String) && !level["effort"].empty?
|
|
219
|
+
end.uniq
|
|
220
|
+
if efforts.any?
|
|
221
|
+
reasoning = {"supported_efforts" => efforts}
|
|
222
|
+
if efforts.include?(default_effort = model["default_reasoning_level"])
|
|
223
|
+
reasoning["default_effort"] = default_effort
|
|
224
|
+
end
|
|
225
|
+
metadata["reasoning"] = reasoning
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
{
|
|
229
|
+
"id" => id,
|
|
230
|
+
"object" => "model",
|
|
231
|
+
"created" => 0,
|
|
232
|
+
"owned_by" => "codex",
|
|
233
|
+
"laiya" => metadata,
|
|
234
|
+
}
|
|
235
|
+
end
|
|
236
|
+
|
|
110
237
|
def request_codex(payload, request, credentials)
|
|
111
238
|
headers = Protocol::HTTP::Headers[
|
|
112
239
|
"accept" => "text/event-stream",
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
+
require "json"
|
|
7
|
+
|
|
6
8
|
require_relative "openai"
|
|
7
9
|
|
|
8
10
|
module Laiya
|
|
@@ -17,6 +19,90 @@ module Laiya
|
|
|
17
19
|
def initialize(endpoint: DEFAULT_ENDPOINT, api_key: "ollama", **options)
|
|
18
20
|
super(endpoint: endpoint, api_key: api_key, **options)
|
|
19
21
|
end
|
|
22
|
+
|
|
23
|
+
# Fetch Ollama's model list and add supported thinking metadata.
|
|
24
|
+
# @returns [Protocol::HTTP::Response] The enriched OpenAI-compatible model list.
|
|
25
|
+
def models
|
|
26
|
+
response = super
|
|
27
|
+
return response unless response.status >= 200 && response.status < 300
|
|
28
|
+
|
|
29
|
+
enrich_models(response)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def enrich_models(response)
|
|
35
|
+
payload = JSON.parse(response.read)
|
|
36
|
+
models = payload.fetch("data")
|
|
37
|
+
unless models.is_a?(Array)
|
|
38
|
+
return model_catalog_error
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
payload["data"] = models.map{|model| enrich_model(model)}
|
|
42
|
+
return Protocol::HTTP::Response[
|
|
43
|
+
200,
|
|
44
|
+
{"content-type" => "application/json"},
|
|
45
|
+
[JSON.dump(payload)],
|
|
46
|
+
]
|
|
47
|
+
rescue JSON::ParserError, KeyError, TypeError
|
|
48
|
+
model_catalog_error
|
|
49
|
+
ensure
|
|
50
|
+
response.close
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def enrich_model(model)
|
|
54
|
+
return model unless model.is_a?(Hash)
|
|
55
|
+
return model unless model["id"].is_a?(String) && !model["id"].empty?
|
|
56
|
+
|
|
57
|
+
response = self.call(Protocol::HTTP::Request[
|
|
58
|
+
"POST",
|
|
59
|
+
"/api/show",
|
|
60
|
+
{"content-type" => "application/json"},
|
|
61
|
+
[JSON.dump(model: model["id"])],
|
|
62
|
+
])
|
|
63
|
+
return model unless response.status >= 200 && response.status < 300
|
|
64
|
+
|
|
65
|
+
thinking = JSON.parse(response.read)["thinking"]
|
|
66
|
+
reasoning = normalize_reasoning(thinking)
|
|
67
|
+
return model unless reasoning
|
|
68
|
+
|
|
69
|
+
metadata = model["laiya"].is_a?(Hash) ? model["laiya"].dup : {}
|
|
70
|
+
metadata["reasoning"] = reasoning
|
|
71
|
+
return model.merge("laiya" => metadata)
|
|
72
|
+
rescue StandardError
|
|
73
|
+
return model
|
|
74
|
+
ensure
|
|
75
|
+
response&.close
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def normalize_reasoning(thinking)
|
|
79
|
+
return unless thinking.is_a?(Hash)
|
|
80
|
+
|
|
81
|
+
values = Array(thinking["values"]).select do |value|
|
|
82
|
+
(value.is_a?(String) && !value.empty?) || value == true || value == false
|
|
83
|
+
end.uniq
|
|
84
|
+
return if values.empty? || values == [false]
|
|
85
|
+
|
|
86
|
+
result = {"thinking_values" => values}
|
|
87
|
+
default = thinking["default"]
|
|
88
|
+
result["default_thinking"] = default if values.include?(default)
|
|
89
|
+
|
|
90
|
+
efforts = values.grep(String)
|
|
91
|
+
unless efforts.empty?
|
|
92
|
+
result["supported_efforts"] = efforts
|
|
93
|
+
result["default_effort"] = default if efforts.include?(default)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
return result
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def model_catalog_error
|
|
100
|
+
Protocol::HTTP::Response[
|
|
101
|
+
502,
|
|
102
|
+
{"content-type" => "application/json"},
|
|
103
|
+
[JSON.dump(error: {message: "Ollama model catalog was invalid", type: "server_error"})],
|
|
104
|
+
]
|
|
105
|
+
end
|
|
20
106
|
end
|
|
21
107
|
end
|
|
22
108
|
end
|
data/lib/laiya/version.rb
CHANGED
data/readme.md
CHANGED
|
@@ -30,6 +30,12 @@ Please see the [project documentation](https://socketry.github.io/laiya/) for mo
|
|
|
30
30
|
|
|
31
31
|
Please see the [project releases](https://socketry.github.io/laiya/releases/index) for all releases.
|
|
32
32
|
|
|
33
|
+
### v0.0.3
|
|
34
|
+
|
|
35
|
+
- Enrich discovered Ollama models with thinking controls from `/api/show`.
|
|
36
|
+
- Discover account-visible Codex models and reasoning capabilities from the authenticated model catalog.
|
|
37
|
+
- Document a Responses-compatible OpenCode provider for Codex tool calling.
|
|
38
|
+
|
|
33
39
|
### v0.0.2
|
|
34
40
|
|
|
35
41
|
- Add Laiya's OpenAI-compatible HTTP API and Async::Service launcher.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.0.3
|
|
4
|
+
|
|
5
|
+
- Enrich discovered Ollama models with thinking controls from `/api/show`.
|
|
6
|
+
- Discover account-visible Codex models and reasoning capabilities from the authenticated model catalog.
|
|
7
|
+
- Document a Responses-compatible OpenCode provider for Codex tool calling.
|
|
8
|
+
|
|
3
9
|
## v0.0.2
|
|
4
10
|
|
|
5
11
|
- Add Laiya's OpenAI-compatible HTTP API and Async::Service launcher.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
metadata.gz.sig
CHANGED
|
Binary file
|