openclacky 1.5.18 → 1.5.19
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
- data/CHANGELOG.md +41 -0
- data/docs/session-observation-hooks.md +86 -0
- data/lib/clacky/agent/chunk_index.rb +4 -1
- data/lib/clacky/agent/history_navigation.rb +6 -1
- data/lib/clacky/agent/hook_manager.rb +11 -4
- data/lib/clacky/agent.rb +39 -3
- data/lib/clacky/billing/platform_billing.rb +9 -0
- data/lib/clacky/default_extensions/advisor/hooks/advisor.rb +2 -1
- data/lib/clacky/default_extensions/advisor/hooks/advisor_complete.rb +3 -2
- data/lib/clacky/default_skills/channel-manager/SKILL.md +70 -9
- data/lib/clacky/default_skills/channel-manager/qq_setup.rb +79 -0
- data/lib/clacky/default_skills/media-gen/SKILL.md +45 -14
- data/lib/clacky/locales/en.rb +1 -1
- data/lib/clacky/locales/zh.rb +1 -1
- data/lib/clacky/media/openai_compat.rb +89 -2
- data/lib/clacky/message_format/bedrock.rb +5 -2
- data/lib/clacky/providers.rb +46 -27
- data/lib/clacky/server/channel/adapters/qq/adapter.rb +662 -0
- data/lib/clacky/server/channel/adapters/qq/api_client.rb +245 -0
- data/lib/clacky/server/channel/adapters/qq/gateway_client.rb +340 -0
- data/lib/clacky/server/channel/adapters/qq/qr_binder.rb +140 -0
- data/lib/clacky/server/channel/channel_config.rb +9 -0
- data/lib/clacky/server/channel/channel_ui_controller.rb +6 -2
- data/lib/clacky/server/channel.rb +1 -0
- data/lib/clacky/server/http_server.rb +116 -11
- data/lib/clacky/server/model_prices.rb +11 -3
- data/lib/clacky/shell_hook_loader.rb +3 -1
- data/lib/clacky/utils/model_pricing.rb +160 -20
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +357 -180
- data/lib/clacky/web/components/chat-navigator.js +154 -171
- data/lib/clacky/web/components/composer.js +11 -2
- data/lib/clacky/web/components/model-picker.js +133 -23
- data/lib/clacky/web/core/aside.js +72 -0
- data/lib/clacky/web/features/billing/store.js +1 -1
- data/lib/clacky/web/features/billing/view.js +126 -34
- data/lib/clacky/web/features/channels/store.js +25 -0
- data/lib/clacky/web/features/channels/view.js +122 -5
- data/lib/clacky/web/features/skills/view.js +12 -0
- data/lib/clacky/web/features/workspace/view.js +88 -12
- data/lib/clacky/web/i18n.js +64 -22
- data/lib/clacky/web/index.html +15 -1
- data/lib/clacky/web/projects.js +131 -3
- data/lib/clacky/web/sessions.js +65 -9
- data/lib/clacky/web/settings.js +19 -0
- metadata +7 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: media-gen
|
|
3
3
|
name_zh: 媒体生成
|
|
4
|
-
description_zh:
|
|
5
|
-
description: 'Generate or edit images, videos, or audio
|
|
4
|
+
description_zh: 生成或编辑图片、视频和音频,适用于封面、海报、插画、配音、环境音等。
|
|
5
|
+
description: 'Generate or edit images, videos, or audio in the current task. Use whenever the user asks to create/generate/produce or edit/modify a picture / image / illustration / cover / poster / icon / artwork, a video / clip / animation, or speech / voiceover / narration / TTS / ambient audio / soundscapes — e.g. generate image, draw, design a cover, edit this image, change the background, text-to-video, generate speech; 画一张, 配图, 编辑图片, 改图, 换背景, 做个视频, 配音, 文字转语音, 环境音. Also use when a document (slides, poster, README hero) needs an inline image.'
|
|
6
6
|
disable-model-invocation: false
|
|
7
7
|
user-invocable: true
|
|
8
8
|
always-show: true
|
|
@@ -71,6 +71,12 @@ curl -s -X POST http://${CLACKY_SERVER_HOST}:${CLACKY_SERVER_PORT}/api/media/ima
|
|
|
71
71
|
- If a call fails with `400 / INVALID_ARGUMENT`, drop the `aspect_ratio` field and retry once before reporting the error.
|
|
72
72
|
- If a call fails with `unknown image model` (400), the configured model name isn't recognized by its backend — tell the user to fix the model name in the settings page; do NOT guess another name and retry.
|
|
73
73
|
|
|
74
|
+
### Content-safety refusal (content_filter)
|
|
75
|
+
|
|
76
|
+
If a call fails with no image and mentions `content_filter` / `safety` / `blocked` (or HTTP `422`), the prompt was blocked. Don't resend as-is — **rephrase the prompt** (soften the sensitive part, keep the intent) and retry, up to 3 times, checking each returned image. Still blocked? Stop and ask the user to adjust.
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
74
80
|
### Request fields
|
|
75
81
|
|
|
76
82
|
| Field | Required | Values | Notes |
|
|
@@ -82,6 +88,11 @@ curl -s -X POST http://${CLACKY_SERVER_HOST}:${CLACKY_SERVER_PORT}/api/media/ima
|
|
|
82
88
|
| `image` | no | file path / base64 / data URL | A single input image to **edit**. Triggers image-edit mode (see below). |
|
|
83
89
|
| `images` | no | array of the above | Multiple input images for a multi-image edit. Takes precedence over `image`. |
|
|
84
90
|
|
|
91
|
+
### Input / reference image format
|
|
92
|
+
|
|
93
|
+
Images passed as `image` / `images` must be **PNG, JPEG, or WebP**. Other formats (SVG, GIF, BMP, TIFF, …) are rejected upstream and fail the call — convert them to one of the supported formats first.
|
|
94
|
+
|
|
95
|
+
|
|
85
96
|
### Editing an existing image
|
|
86
97
|
|
|
87
98
|
To edit instead of generate from scratch, pass the existing image as `image`
|
|
@@ -313,14 +324,16 @@ Rules & caveats:
|
|
|
313
324
|
chaining is the practical option today; Veo's native server-side `extend`
|
|
314
325
|
(148s) is not wired into this endpoint yet.
|
|
315
326
|
|
|
316
|
-
### Seedance
|
|
327
|
+
### Seedance — multimodal video
|
|
317
328
|
|
|
318
329
|
When the configured `type=video` model is a ByteDance **Doubao Seedance**
|
|
319
|
-
model
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
330
|
+
model, the **same** `POST /api/media/video` endpoint drives it. No separate
|
|
331
|
+
endpoint — the server routes by Base URL automatically. Seedance adds richer
|
|
332
|
+
inputs on top of the common fields (all optional, Seedance-only), split below
|
|
333
|
+
into a **common** set that works on any Seedance gateway and an **Ark-only**
|
|
334
|
+
set that takes effect only on the native Volcengine Ark transport (Base URL
|
|
335
|
+
under `*.volces.com`). On any other OpenAI-compatible Seedance gateway, stick
|
|
336
|
+
to the common fields.
|
|
324
337
|
|
|
325
338
|
> **Cost gate — ask before EVERY generation.** Resolution is the main driver
|
|
326
339
|
> of Seedance's price (4k costs far more than 720p). So **once you've confirmed
|
|
@@ -337,22 +350,34 @@ common fields; all are optional and only apply to Seedance:
|
|
|
337
350
|
> **When editing or continuing/extending an existing video, default to that
|
|
338
351
|
> source video's resolution — never silently upgrade it (e.g. don't turn a
|
|
339
352
|
> 720p source into a 4k render).** If the user gave no answer and you didn't
|
|
340
|
-
> ask, the server pins `720p`. **These Seedance-only fields
|
|
341
|
-
>
|
|
342
|
-
>
|
|
343
|
-
>
|
|
353
|
+
> ask, the server pins `720p`. **These Seedance-only fields have NO effect on
|
|
354
|
+
> Veo or Qwen/DashScope backends — never send them there.** (And within
|
|
355
|
+
> Seedance, the Ark-only table additionally requires a `*.volces.com` Base
|
|
356
|
+
> URL.)
|
|
357
|
+
|
|
358
|
+
**Common fields** — work on any Seedance gateway (native Ark or an
|
|
359
|
+
OpenAI-compatible host):
|
|
344
360
|
|
|
345
361
|
| Field | Values | Notes |
|
|
346
362
|
|--------------------|------------------------------------------|-------|
|
|
347
363
|
| `aspect_ratio` | `landscape`/`portrait`/`square`, or a raw Ark ratio like `16:9`, `9:16`, `4:3`, `3:4`, `21:9`, `adaptive` | Raw ratios pass through unchanged. |
|
|
348
364
|
| `duration_seconds` | integer, or `-1` | `-1` lets the model pick the length (Seedance 2.0 / 1.5 Pro). |
|
|
365
|
+
| `first_frame` | media ref (see below) | First frame → image-to-video. |
|
|
366
|
+
| `reference_images` | array of media refs (0–9) | Reference images. |
|
|
367
|
+
|
|
368
|
+
**Ark-only fields** — only take effect on the native Volcengine Ark transport
|
|
369
|
+
(`video.base_url` under `*.volces.com`). On any other OpenAI-compatible
|
|
370
|
+
Seedance gateway, `resolution` / `generate_audio` / `watermark` / `seed` are
|
|
371
|
+
silently ignored, and `last_frame` / `reference_videos` / `reference_audios`
|
|
372
|
+
are rejected outright — so don't send any of them there:
|
|
373
|
+
|
|
374
|
+
| Field | Values | Notes |
|
|
375
|
+
|--------------------|------------------------------------------|-------|
|
|
349
376
|
| `resolution` | `480p` / `720p` / `1080p` / `4k` | **Defaults to `720p` when omitted** (cost control). Ask the user before every generation — never reuse a prior answer. See the cost gate above. Model-dependent; unsupported values are rejected upstream. |
|
|
350
377
|
| `generate_audio` | `true` / `false` | Seedance 2.0 / 1.5 Pro can synthesize a synced audio track. |
|
|
351
378
|
| `watermark` | `true` / `false` | |
|
|
352
379
|
| `seed` | integer | Reproducibility. |
|
|
353
|
-
| `first_frame` | media ref (see below) | First frame → image-to-video. |
|
|
354
380
|
| `last_frame` | media ref | Together with `first_frame` → first+last-frame video. |
|
|
355
|
-
| `reference_images` | array of media refs (0–9) | Reference images. |
|
|
356
381
|
| `reference_videos` | array of media refs (0–3) | Reference videos. |
|
|
357
382
|
| `reference_audios` | array of media refs (0–3) | Reference audio (background music / voice). |
|
|
358
383
|
|
|
@@ -482,6 +507,12 @@ done
|
|
|
482
507
|
polling and tell the user the task is still rendering in the background,
|
|
483
508
|
give them the `task_id`, and let them check again later — do NOT resubmit.
|
|
484
509
|
|
|
510
|
+
## Generating ambient audio
|
|
511
|
+
|
|
512
|
+
Ambient audio includes rain, ocean waves, wind, and café background
|
|
513
|
+
sounds. Generate it programmatically, verify the audio, and provide
|
|
514
|
+
it for playback or download.
|
|
515
|
+
|
|
485
516
|
## Generating speech (Gemini TTS)
|
|
486
517
|
|
|
487
518
|
The same `/api/media/` namespace serves text-to-speech. The user must
|
data/lib/clacky/locales/en.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Clacky
|
|
|
6
6
|
"phase.skill_evolution" => "Reflecting on this task",
|
|
7
7
|
"phase.memory_update" => "Updating long-term memory",
|
|
8
8
|
"llm.error.insufficient_credit" => "Insufficient credit, please top up your account to continue",
|
|
9
|
-
"llm.error.rate_limit_400" => "
|
|
9
|
+
"llm.error.rate_limit_400" => "Too many requests or model temporarily unavailable. Please switch to another model and retry",
|
|
10
10
|
"llm.error.invalid_api_key" => "API key is invalid or expired, please update it in Settings",
|
|
11
11
|
"llm.error.403.model_not_allowed" => "Free trial only supports the or-gemini-3-8-flash model. Switch to that model, or top up to use other models",
|
|
12
12
|
"llm.error.403.api_key_revoked" => "API key has been revoked, please generate a new one",
|
data/lib/clacky/locales/zh.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Clacky
|
|
|
6
6
|
"phase.skill_evolution" => "正在复盘本次任务",
|
|
7
7
|
"phase.memory_update" => "正在更新长期记忆",
|
|
8
8
|
"llm.error.insufficient_credit" => "API Key 账户余额不足,请前往控制台充值后继续使用",
|
|
9
|
-
"llm.error.rate_limit_400" => "
|
|
9
|
+
"llm.error.rate_limit_400" => "请求频率过高或模型暂时不可用,请暂时切换其他模型重试",
|
|
10
10
|
"llm.error.invalid_api_key" => "API Key 无效或已过期,请到设置中重新配置",
|
|
11
11
|
"llm.error.403.model_not_allowed" => "免费试用仅支持 or-gemini-3-8-flash 模型,请切换到该模型,或充值后使用其他模型",
|
|
12
12
|
"llm.error.403.api_key_revoked" => "API 密钥已被撤销,请前往控制台重新生成",
|
|
@@ -160,7 +160,10 @@ module Clacky
|
|
|
160
160
|
)
|
|
161
161
|
end
|
|
162
162
|
|
|
163
|
-
def generate_video(prompt:, aspect_ratio: DEFAULT_ASPECT, duration_seconds: nil, output_dir: nil,
|
|
163
|
+
def generate_video(prompt:, aspect_ratio: DEFAULT_ASPECT, duration_seconds: nil, output_dir: nil,
|
|
164
|
+
image: nil, first_frame: nil, last_frame: nil,
|
|
165
|
+
reference_images: nil, reference_videos: nil, reference_audios: nil,
|
|
166
|
+
**_kwargs)
|
|
164
167
|
provider_id = Clacky::Providers.find_by_base_url(@base_url) || "custom"
|
|
165
168
|
aspect = VIDEO_ASPECTS.include?(aspect_ratio) ? aspect_ratio : DEFAULT_ASPECT
|
|
166
169
|
duration = duration_seconds.to_i
|
|
@@ -179,8 +182,42 @@ module Clacky
|
|
|
179
182
|
)
|
|
180
183
|
end
|
|
181
184
|
|
|
185
|
+
# The OpenAI-compatible video endpoint has two independent image channels:
|
|
186
|
+
# • the first frame (exact anchor), carried as `image` — Veo-style
|
|
187
|
+
# image-to-video, and the only thing this facade honored before.
|
|
188
|
+
# • reference images (non-exact subject/identity/style guidance),
|
|
189
|
+
# carried as `input_references` — forwarded to Seedance 2.x's
|
|
190
|
+
# reference-to-video. `last_frame` and video/audio references are
|
|
191
|
+
# NOT representable on this facade (only the native Volcengine Ark
|
|
192
|
+
# path supports them); callers wanting those must use an Ark model.
|
|
193
|
+
# A bare `first_frame` (no explicit `image`) acts as the first frame.
|
|
194
|
+
first_frame_image = normalize_video_first_frame(image, first_frame)
|
|
195
|
+
begin
|
|
196
|
+
references = normalize_video_references(reference_images)
|
|
197
|
+
rescue ArgumentError => e
|
|
198
|
+
return video_error_response(
|
|
199
|
+
error: e.message,
|
|
200
|
+
error_type: "invalid_argument", provider: provider_id, prompt: prompt, aspect_ratio: aspect
|
|
201
|
+
)
|
|
202
|
+
end
|
|
203
|
+
unsupported = []
|
|
204
|
+
unsupported << "last_frame" unless to_list(last_frame).empty?
|
|
205
|
+
unsupported << "reference_videos" unless to_list(reference_videos).empty?
|
|
206
|
+
unsupported << "reference_audios" unless to_list(reference_audios).empty?
|
|
207
|
+
unless unsupported.empty?
|
|
208
|
+
return video_error_response(
|
|
209
|
+
error: "#{unsupported.join('/')} #{unsupported.length == 1 ? 'is' : 'are'} only " \
|
|
210
|
+
"supported by the native Volcengine (Seedance Ark) video backend, not by this " \
|
|
211
|
+
"OpenAI-compatible video model '#{@model}'. Use first_frame and/or " \
|
|
212
|
+
"reference_images here, or configure a *.volces.com video model for " \
|
|
213
|
+
"last_frame / reference_videos / reference_audios.",
|
|
214
|
+
error_type: "invalid_argument", provider: provider_id, prompt: prompt, aspect_ratio: aspect
|
|
215
|
+
)
|
|
216
|
+
end
|
|
217
|
+
|
|
182
218
|
payload = { model: @model, prompt: prompt, aspect_ratio: aspect, duration_seconds: duration }
|
|
183
|
-
payload[:image] =
|
|
219
|
+
payload[:image] = first_frame_image unless first_frame_image.nil?
|
|
220
|
+
payload[:input_references] = references unless references.empty?
|
|
184
221
|
|
|
185
222
|
begin
|
|
186
223
|
response = video_connection.post("videos/generations") do |req|
|
|
@@ -547,6 +584,56 @@ module Clacky
|
|
|
547
584
|
end
|
|
548
585
|
end
|
|
549
586
|
|
|
587
|
+
# Coerce one video media ref into the first-frame image hash the gateway's
|
|
588
|
+
# /videos endpoint expects: { "b64_json" => "<base64 or data URL>",
|
|
589
|
+
# "mime_type" => "image/..." }. An explicit `image` (already that hash)
|
|
590
|
+
# wins; otherwise a `first_frame` string (file path / data URL / bare
|
|
591
|
+
# base64) or hash is accepted. Returns nil when neither is given.
|
|
592
|
+
private def normalize_video_first_frame(image, first_frame)
|
|
593
|
+
if image.is_a?(Hash) && !image["b64_json"].to_s.strip.empty?
|
|
594
|
+
return image
|
|
595
|
+
end
|
|
596
|
+
source = first_frame
|
|
597
|
+
source = first_frame.first if first_frame.is_a?(Array) && !first_frame.empty?
|
|
598
|
+
return nil if source.nil?
|
|
599
|
+
|
|
600
|
+
if source.is_a?(Hash)
|
|
601
|
+
return source unless source["b64_json"].to_s.strip.empty?
|
|
602
|
+
return nil
|
|
603
|
+
end
|
|
604
|
+
s = source.to_s.strip
|
|
605
|
+
return nil if s.empty?
|
|
606
|
+
{ "b64_json" => to_data_url(s) }
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
# Normalise reference images into the gateway `input_references` array:
|
|
610
|
+
# a list of data-URL / https strings. Accepts a single value or an array,
|
|
611
|
+
# where each entry is a file path, a data URL, a bare base64 string, an
|
|
612
|
+
# http(s) URL, or a { "b64_json", "mime_type" } hash. Raises ArgumentError
|
|
613
|
+
# on an undecodable local input (same contract as normalize_input_images).
|
|
614
|
+
private def normalize_video_references(reference_images)
|
|
615
|
+
to_list(reference_images).filter_map do |item|
|
|
616
|
+
if item.is_a?(Hash)
|
|
617
|
+
b64 = item["b64_json"].to_s.strip
|
|
618
|
+
next if b64.empty?
|
|
619
|
+
next b64 if b64.start_with?("data:")
|
|
620
|
+
mime = item["mime_type"].to_s.strip
|
|
621
|
+
mime = "image/png" if mime.empty?
|
|
622
|
+
next "data:#{mime};base64,#{b64}"
|
|
623
|
+
end
|
|
624
|
+
s = item.to_s.strip
|
|
625
|
+
next if s.empty?
|
|
626
|
+
next s if s.start_with?("http://", "https://")
|
|
627
|
+
to_data_url(s)
|
|
628
|
+
end
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
# Coerce a nil / scalar / array value into an array, dropping a nil.
|
|
632
|
+
private def to_list(value)
|
|
633
|
+
return [] if value.nil?
|
|
634
|
+
value.is_a?(Array) ? value : [value]
|
|
635
|
+
end
|
|
636
|
+
|
|
550
637
|
private def to_data_url(input)
|
|
551
638
|
return input if input.start_with?("data:")
|
|
552
639
|
|
|
@@ -88,15 +88,18 @@ module Clacky
|
|
|
88
88
|
body[:toolConfig] = { tools: tools.map { |t| to_api_tool(t) } }
|
|
89
89
|
end
|
|
90
90
|
|
|
91
|
-
extra = additional_fields_for_effort(reasoning_effort)
|
|
91
|
+
extra = additional_fields_for_effort(reasoning_effort, model)
|
|
92
92
|
body[:additionalModelRequestFields] = extra if extra
|
|
93
93
|
|
|
94
94
|
body
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
-
private_class_method def self.additional_fields_for_effort(effort)
|
|
97
|
+
private_class_method def self.additional_fields_for_effort(effort, model = nil)
|
|
98
98
|
return nil if effort.nil? || effort.to_s.empty?
|
|
99
99
|
return nil unless %w[low medium high xhigh max].include?(effort.to_s)
|
|
100
|
+
# Converse accepts thinking/output_config only for the Anthropic family;
|
|
101
|
+
# other Converse models (Z.AI GLM, DeepSeek) reject unknown fields.
|
|
102
|
+
return nil unless model.to_s.downcase.include?("claude")
|
|
100
103
|
{
|
|
101
104
|
thinking: { type: "adaptive" },
|
|
102
105
|
output_config: { effort: effort.to_s }
|
data/lib/clacky/providers.rb
CHANGED
|
@@ -36,23 +36,28 @@ module Clacky
|
|
|
36
36
|
"default_model" => "auto",
|
|
37
37
|
"models" => [
|
|
38
38
|
"auto",
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"abs-gpt-5.6-luna",
|
|
39
|
+
"dsk-deepseek-flash",
|
|
40
|
+
"dsk-deepseek-v4-pro",
|
|
41
|
+
"oc-glm-5.3",
|
|
42
|
+
"oc-kimi-k3",
|
|
43
|
+
"oc-minimax-m2.7",
|
|
45
44
|
"abs-claude-fable-5-1",
|
|
46
45
|
"abs-claude-fable-5",
|
|
47
46
|
"abs-claude-opus-5-5",
|
|
48
47
|
"abs-claude-opus-5",
|
|
49
48
|
"abs-claude-opus-4-8",
|
|
50
49
|
"abs-claude-opus-4-7",
|
|
50
|
+
"abs-claude-sonnet-5-5",
|
|
51
51
|
"abs-claude-sonnet-5",
|
|
52
52
|
"abs-claude-sonnet-4-6",
|
|
53
53
|
"abs-claude-haiku-4-5",
|
|
54
|
-
"
|
|
55
|
-
"
|
|
54
|
+
"abs-gpt-6-astra",
|
|
55
|
+
"abs-gpt-6.1-sol",
|
|
56
|
+
"abs-gpt-6-sol",
|
|
57
|
+
"abs-gpt-6-luna",
|
|
58
|
+
"abs-gpt-5.6-sol",
|
|
59
|
+
"abs-gpt-5.6-terra",
|
|
60
|
+
"abs-gpt-5.6-luna",
|
|
56
61
|
"or-gemini-3-1-pro",
|
|
57
62
|
"or-gemini-3-8-flash",
|
|
58
63
|
"or-gemini-3-7-flash"
|
|
@@ -141,9 +146,17 @@ module Clacky
|
|
|
141
146
|
# images uploaded for text-only models must be downgraded to disk
|
|
142
147
|
# refs. Gemini 3.1 Pro keeps the provider-default vision=true
|
|
143
148
|
# (it accepts image/audio/video input natively via OpenRouter).
|
|
149
|
+
# oc-glm-5.3 is likewise text-only: its upstream rejects any content
|
|
150
|
+
# block other than `type: "text"`, so images must never be sent
|
|
151
|
+
# inline. oc-minimax-m2.7 is also text-only, but its upstream accepts
|
|
152
|
+
# the image block and silently ignores it — a worse failure mode,
|
|
153
|
+
# since the model then answers as if no image had been attached.
|
|
154
|
+
# oc-kimi-k3 is natively multimodal and keeps vision=true.
|
|
144
155
|
"model_capabilities" => {
|
|
145
156
|
"dsk-deepseek-flash" => { "vision" => true }.freeze,
|
|
146
|
-
"dsk-deepseek-v4-pro" => { "vision" => false }.freeze
|
|
157
|
+
"dsk-deepseek-v4-pro" => { "vision" => false }.freeze,
|
|
158
|
+
"oc-glm-5.3" => { "vision" => false }.freeze,
|
|
159
|
+
"oc-minimax-m2.7" => { "vision" => false }.freeze
|
|
147
160
|
}.freeze,
|
|
148
161
|
# Bedrock GPT models (abs-gpt-*) are served through the OpenAI
|
|
149
162
|
# Responses API — their Chat Completions endpoint rejects function
|
|
@@ -166,6 +179,7 @@ module Clacky
|
|
|
166
179
|
"abs-claude-opus-5" => "abs-claude-haiku-4-5",
|
|
167
180
|
"abs-claude-opus-4-8" => "abs-claude-haiku-4-5",
|
|
168
181
|
"abs-claude-opus-4-7" => "abs-claude-haiku-4-5",
|
|
182
|
+
"abs-claude-sonnet-5-5" => "abs-claude-haiku-4-5",
|
|
169
183
|
"abs-claude-sonnet-5" => "abs-claude-haiku-4-5",
|
|
170
184
|
"abs-claude-sonnet-4-6" => "abs-claude-haiku-4-5",
|
|
171
185
|
"abs-gpt-6-astra" => "abs-gpt-6-luna",
|
|
@@ -182,6 +196,7 @@ module Clacky
|
|
|
182
196
|
"abs-claude-fable-5" => "abs-claude-opus-5",
|
|
183
197
|
"abs-claude-opus-5-5" => "abs-claude-opus-5",
|
|
184
198
|
"abs-claude-opus-5" => "abs-claude-opus-4-8",
|
|
199
|
+
"abs-claude-sonnet-5-5" => "abs-claude-sonnet-5",
|
|
185
200
|
"abs-claude-sonnet-5" => "abs-claude-sonnet-4-6"
|
|
186
201
|
},
|
|
187
202
|
# Selectable endpoints exposed in the Base URL dropdown. The gateway
|
|
@@ -515,27 +530,29 @@ module Clacky
|
|
|
515
530
|
"name" => "MiMo (Xiaomi)",
|
|
516
531
|
"base_url" => "https://api.xiaomimimo.com/v1",
|
|
517
532
|
"api" => "openai-completions",
|
|
518
|
-
"default_model" => "mimo-v2.
|
|
519
|
-
# The
|
|
520
|
-
#
|
|
521
|
-
#
|
|
522
|
-
# - mimo-v2.
|
|
523
|
-
# - mimo-v2.
|
|
524
|
-
#
|
|
525
|
-
|
|
526
|
-
|
|
533
|
+
"default_model" => "mimo-v2.6-pro",
|
|
534
|
+
# The V2.6 chat models are both native omni-modal models. Keep the V2.5
|
|
535
|
+
# ids available during Xiaomi's migration window; V2.5-Pro remains the
|
|
536
|
+
# only text-only model in this preset.
|
|
537
|
+
# - mimo-v2.6-pro: flagship omni-modal reasoning model
|
|
538
|
+
# - mimo-v2.6-flash: efficient omni-modal reasoning model
|
|
539
|
+
# - mimo-v2.5-pro: legacy text reasoning flagship, no vision
|
|
540
|
+
# - mimo-v2.5: legacy native omni-modal model
|
|
541
|
+
# Source: https://mimo.mi.com/docs/zh-CN/updates/model
|
|
542
|
+
"models" => ["mimo-v2.6-pro", "mimo-v2.6-flash", "mimo-v2.5-pro", "mimo-v2.5"],
|
|
543
|
+
"capabilities" => { "vision" => true }.freeze,
|
|
527
544
|
"model_capabilities" => {
|
|
528
|
-
"mimo-v2.5" => { "vision" =>
|
|
545
|
+
"mimo-v2.5-pro" => { "vision" => false }.freeze
|
|
529
546
|
}.freeze,
|
|
530
|
-
"default_ocr_model" => "mimo-v2.
|
|
531
|
-
# Xiaomi serves the same
|
|
547
|
+
"default_ocr_model" => "mimo-v2.6-flash",
|
|
548
|
+
# Xiaomi serves the same chat lineup from two billing endpoints: the
|
|
532
549
|
# pay-as-you-go API (api.xiaomimimo.com) and the Token Plan subscription
|
|
533
550
|
# endpoint (token-plan-cn.xiaomimimo.com). Both accept identical model
|
|
534
551
|
# ids and share one capability profile, so a single preset with
|
|
535
552
|
# endpoint_variants recognises both. Without this, users on the Token
|
|
536
553
|
# Plan endpoint fell through to "unknown provider" and the conservative
|
|
537
554
|
# vision=true default applied to every model — sending images to the
|
|
538
|
-
# text-only mimo-v2.5-pro, which rejects image input.
|
|
555
|
+
# text-only legacy mimo-v2.5-pro, which rejects image input.
|
|
539
556
|
"endpoint_variants" => [
|
|
540
557
|
{ "label" => "Pay-as-you-go", "label_key" => "settings.models.baseurl.variant.mimo_payg", "base_url" => "https://api.xiaomimimo.com/v1", "region" => "cn" }.freeze,
|
|
541
558
|
{ "label" => "Token Plan", "label_key" => "settings.models.baseurl.variant.mimo_token_plan", "base_url" => "https://token-plan-cn.xiaomimimo.com/v1", "region" => "cn" }.freeze
|
|
@@ -791,17 +808,19 @@ module Clacky
|
|
|
791
808
|
# Per-model maximum output token limits.
|
|
792
809
|
#
|
|
793
810
|
# The Agent global default (@max_tokens = 16_384) is tuned for the lowest
|
|
794
|
-
# common denominator. Strong reasoning models (GLM-5.2, Kimi-K3,
|
|
795
|
-
# support 64K–128K output but are artificially throttled to
|
|
796
|
-
# truncation on long reasoning chains and code generation.
|
|
811
|
+
# common denominator. Strong reasoning models (GLM-5.2, Kimi-K3, MiniMax-M2.7,
|
|
812
|
+
# MiMo-V2.5/V2.6) support 64K–128K output but are artificially throttled to
|
|
813
|
+
# 16K, causing truncation on long reasoning chains and code generation.
|
|
797
814
|
#
|
|
798
815
|
# Entries are matched top-to-bottom; the first match wins. Models not
|
|
799
816
|
# listed here fall back to the global default.
|
|
800
817
|
MODEL_MAX_OUTPUT = [
|
|
801
818
|
{ pattern: /glm/i, limit: 65_536 }, # GLM-5.2: 128K output ceiling; 64K ample for reasoning+answer
|
|
802
819
|
{ pattern: /kimi-k3/i, limit: 65_536 }, # Kimi K3: max_completion_tokens=131072 (max 1M); 64K ample
|
|
803
|
-
{ pattern: /
|
|
804
|
-
{ pattern: /
|
|
820
|
+
{ pattern: /minimax-m2\.7/i, limit: 65_536 }, # MiniMax M2.7: max_tokens ceiling 196608; 64K ample
|
|
821
|
+
{ pattern: /\Amimo-v2\.6-(?:pro|flash)\z/i, limit: 65_536 }, # MiMo-V2.6: max_completion_tokens=131072; 64K ample
|
|
822
|
+
{ pattern: /mimo-v2\.5-pro/i, limit: 65_536 }, # MiMo-V2.5-Pro: max_completion_tokens=131072; 64K ample
|
|
823
|
+
{ pattern: /mimo/i, limit: 32_768 }, # MiMo-V2.5: max_completion_tokens=32768; full default ceiling
|
|
805
824
|
{ pattern: /gemini/i, limit: 65_536 } # Gemini 3.x: 64K max output ceiling
|
|
806
825
|
].freeze
|
|
807
826
|
|