openclacky 1.2.8 → 1.2.9
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 +12 -0
- data/lib/clacky/agent_config.rb +91 -7
- data/lib/clacky/client.rb +6 -2
- data/lib/clacky/default_skills/channel-manager/SKILL.md +33 -110
- data/lib/clacky/default_skills/media-gen/SKILL.md +128 -0
- data/lib/clacky/media/base.rb +68 -0
- data/lib/clacky/media/gemini.rb +36 -0
- data/lib/clacky/media/generator.rb +78 -0
- data/lib/clacky/media/openai_compat.rb +168 -0
- data/lib/clacky/providers.rb +82 -0
- data/lib/clacky/server/http_server.rb +210 -20
- data/lib/clacky/telemetry.rb +11 -5
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +172 -12
- data/lib/clacky/web/i18n.js +58 -0
- data/lib/clacky/web/index.html +14 -2
- data/lib/clacky/web/model-tester.js +58 -0
- data/lib/clacky/web/onboard.js +17 -30
- data/lib/clacky/web/settings.js +322 -97
- data/lib/clacky.rb +3 -0
- data/scripts/build/lib/network.sh +61 -30
- data/scripts/install.sh +61 -30
- data/scripts/install_browser.sh +61 -30
- data/scripts/install_full.sh +61 -30
- data/scripts/install_rails_deps.sh +61 -30
- data/scripts/install_system_deps.sh +61 -30
- metadata +7 -2
- data/lib/clacky/default_skills/channel-manager/feishu_setup.rb +0 -574
data/lib/clacky/web/app.css
CHANGED
|
@@ -3858,6 +3858,7 @@ body {
|
|
|
3858
3858
|
.model-card-grid-actions {
|
|
3859
3859
|
display: flex;
|
|
3860
3860
|
flex-direction: row;
|
|
3861
|
+
align-items: center;
|
|
3861
3862
|
flex-wrap: wrap;
|
|
3862
3863
|
gap: 0.5rem;
|
|
3863
3864
|
padding-top: 0.625rem;
|
|
@@ -3867,7 +3868,7 @@ body {
|
|
|
3867
3868
|
display: inline-flex;
|
|
3868
3869
|
align-items: center;
|
|
3869
3870
|
justify-content: center;
|
|
3870
|
-
gap: 0.
|
|
3871
|
+
gap: 0.3125rem;
|
|
3871
3872
|
padding: 0.375rem 0.5rem;
|
|
3872
3873
|
border: 1px solid var(--color-border-primary);
|
|
3873
3874
|
border-radius: 6px;
|
|
@@ -3877,15 +3878,9 @@ body {
|
|
|
3877
3878
|
cursor: pointer;
|
|
3878
3879
|
transition: all 0.15s;
|
|
3879
3880
|
white-space: nowrap;
|
|
3881
|
+
min-width: 0;
|
|
3880
3882
|
}
|
|
3881
|
-
.btn-card-grid-action svg {
|
|
3882
|
-
flex-shrink: 0;
|
|
3883
|
-
}
|
|
3884
|
-
.btn-card-grid-action span {
|
|
3885
|
-
overflow: hidden;
|
|
3886
|
-
text-overflow: ellipsis;
|
|
3887
|
-
white-space: nowrap;
|
|
3888
|
-
}
|
|
3883
|
+
.btn-card-grid-action svg { flex-shrink: 0; }
|
|
3889
3884
|
.btn-card-grid-action:hover:not(:disabled) {
|
|
3890
3885
|
border-color: var(--color-accent-primary);
|
|
3891
3886
|
color: var(--color-accent-primary);
|
|
@@ -3895,13 +3890,22 @@ body {
|
|
|
3895
3890
|
opacity: 0.5;
|
|
3896
3891
|
cursor: not-allowed;
|
|
3897
3892
|
}
|
|
3893
|
+
.btn-card-grid-action-primary {
|
|
3894
|
+
background: var(--color-bg-secondary);
|
|
3895
|
+
color: var(--color-text-primary);
|
|
3896
|
+
font-weight: 500;
|
|
3897
|
+
}
|
|
3898
3898
|
.btn-card-grid-action-danger:hover:not(:disabled) {
|
|
3899
3899
|
border-color: var(--color-error);
|
|
3900
3900
|
color: var(--color-error);
|
|
3901
3901
|
background: color-mix(in srgb, var(--color-error) 8%, transparent);
|
|
3902
3902
|
}
|
|
3903
|
-
|
|
3904
|
-
|
|
3903
|
+
.model-card-grid-toolbar {
|
|
3904
|
+
display: flex;
|
|
3905
|
+
flex-wrap: wrap;
|
|
3906
|
+
gap: 0.375rem;
|
|
3907
|
+
margin-left: auto;
|
|
3908
|
+
justify-content: flex-end;
|
|
3905
3909
|
}
|
|
3906
3910
|
.model-card-grid-footer {
|
|
3907
3911
|
display: flex;
|
|
@@ -9710,4 +9714,160 @@ body.setup-mode[data-theme="dark"] {
|
|
|
9710
9714
|
grid-column: 1 / -1;
|
|
9711
9715
|
text-align: right;
|
|
9712
9716
|
}
|
|
9713
|
-
}
|
|
9717
|
+
}
|
|
9718
|
+
|
|
9719
|
+
/* ════ Media generation (Settings → Models tab) ════ */
|
|
9720
|
+
.settings-section-desc {
|
|
9721
|
+
font-size: 0.8125rem;
|
|
9722
|
+
color: var(--color-text-secondary);
|
|
9723
|
+
line-height: 1.5;
|
|
9724
|
+
margin: -0.25rem 0 0.5rem;
|
|
9725
|
+
}
|
|
9726
|
+
.media-row {
|
|
9727
|
+
border: 1px solid var(--color-border-primary);
|
|
9728
|
+
border-radius: 8px;
|
|
9729
|
+
background: var(--color-bg-secondary);
|
|
9730
|
+
margin-bottom: 0.5rem;
|
|
9731
|
+
overflow: hidden;
|
|
9732
|
+
}
|
|
9733
|
+
.media-row.is-expanded {
|
|
9734
|
+
background: var(--color-bg-secondary);
|
|
9735
|
+
}
|
|
9736
|
+
.media-row-head {
|
|
9737
|
+
display: flex;
|
|
9738
|
+
align-items: center;
|
|
9739
|
+
gap: 0.75rem;
|
|
9740
|
+
padding: 0.5rem 0.75rem;
|
|
9741
|
+
min-height: 2.25rem;
|
|
9742
|
+
}
|
|
9743
|
+
.media-row-title {
|
|
9744
|
+
font-size: 0.875rem;
|
|
9745
|
+
font-weight: 600;
|
|
9746
|
+
color: var(--color-text-primary);
|
|
9747
|
+
min-width: 3rem;
|
|
9748
|
+
}
|
|
9749
|
+
.media-row-segmented {
|
|
9750
|
+
display: inline-flex;
|
|
9751
|
+
background: var(--color-bg-primary);
|
|
9752
|
+
border: 1px solid var(--color-border-primary);
|
|
9753
|
+
border-radius: 6px;
|
|
9754
|
+
padding: 1px;
|
|
9755
|
+
}
|
|
9756
|
+
.media-row-segmented button {
|
|
9757
|
+
background: transparent;
|
|
9758
|
+
border: none;
|
|
9759
|
+
padding: 0.25rem 0.625rem;
|
|
9760
|
+
font-size: 0.75rem;
|
|
9761
|
+
color: var(--color-text-secondary);
|
|
9762
|
+
border-radius: 5px;
|
|
9763
|
+
cursor: pointer;
|
|
9764
|
+
transition: background-color 0.15s ease, color 0.15s ease;
|
|
9765
|
+
font-family: inherit;
|
|
9766
|
+
}
|
|
9767
|
+
.media-row-segmented button:hover:not(.is-active):not(:disabled) {
|
|
9768
|
+
color: var(--color-text-primary);
|
|
9769
|
+
}
|
|
9770
|
+
.media-row-segmented button.is-active {
|
|
9771
|
+
background: var(--color-button-primary);
|
|
9772
|
+
color: #fff;
|
|
9773
|
+
}
|
|
9774
|
+
.media-row-segmented button:disabled {
|
|
9775
|
+
opacity: 0.4;
|
|
9776
|
+
cursor: not-allowed;
|
|
9777
|
+
}
|
|
9778
|
+
.media-row-status {
|
|
9779
|
+
margin-left: auto;
|
|
9780
|
+
font-size: 0.75rem;
|
|
9781
|
+
color: var(--color-text-secondary);
|
|
9782
|
+
font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
9783
|
+
white-space: nowrap;
|
|
9784
|
+
overflow: hidden;
|
|
9785
|
+
text-overflow: ellipsis;
|
|
9786
|
+
max-width: 16rem;
|
|
9787
|
+
}
|
|
9788
|
+
.media-row-detail {
|
|
9789
|
+
border-top: 1px solid var(--color-border-primary);
|
|
9790
|
+
padding: 0.625rem 0.75rem;
|
|
9791
|
+
font-size: 0.8125rem;
|
|
9792
|
+
color: var(--color-text-secondary);
|
|
9793
|
+
line-height: 1.5;
|
|
9794
|
+
background: var(--color-bg-primary);
|
|
9795
|
+
}
|
|
9796
|
+
.media-row-detail.is-warning {
|
|
9797
|
+
color: var(--color-warning, #d97706);
|
|
9798
|
+
}
|
|
9799
|
+
.media-row-hint {
|
|
9800
|
+
margin-top: 0.375rem;
|
|
9801
|
+
font-size: 0.75rem;
|
|
9802
|
+
color: var(--color-text-tertiary, var(--color-text-secondary));
|
|
9803
|
+
}
|
|
9804
|
+
.media-row-detail .media-kv {
|
|
9805
|
+
display: grid;
|
|
9806
|
+
grid-template-columns: max-content 1fr;
|
|
9807
|
+
gap: 0.25rem 0.75rem;
|
|
9808
|
+
font-size: 0.8125rem;
|
|
9809
|
+
}
|
|
9810
|
+
.media-row-detail .media-kv-key {
|
|
9811
|
+
color: var(--color-text-secondary);
|
|
9812
|
+
}
|
|
9813
|
+
.media-row-detail .media-kv-val {
|
|
9814
|
+
color: var(--color-text-primary);
|
|
9815
|
+
font-weight: 500;
|
|
9816
|
+
word-break: break-all;
|
|
9817
|
+
}
|
|
9818
|
+
.media-row-actions {
|
|
9819
|
+
display: flex;
|
|
9820
|
+
gap: 0.5rem;
|
|
9821
|
+
margin-top: 0.5rem;
|
|
9822
|
+
justify-content: flex-end;
|
|
9823
|
+
}
|
|
9824
|
+
.media-row-btn {
|
|
9825
|
+
background: transparent;
|
|
9826
|
+
border: 1px solid var(--color-border-primary);
|
|
9827
|
+
color: var(--color-text-primary);
|
|
9828
|
+
border-radius: 6px;
|
|
9829
|
+
padding: 0.3125rem 0.75rem;
|
|
9830
|
+
font-size: 0.75rem;
|
|
9831
|
+
cursor: pointer;
|
|
9832
|
+
transition: background-color 0.15s ease;
|
|
9833
|
+
font-family: inherit;
|
|
9834
|
+
}
|
|
9835
|
+
.media-row-btn:hover {
|
|
9836
|
+
background: var(--color-bg-secondary);
|
|
9837
|
+
}
|
|
9838
|
+
.media-row-btn.is-primary {
|
|
9839
|
+
background: var(--color-button-primary);
|
|
9840
|
+
color: #fff;
|
|
9841
|
+
border-color: transparent;
|
|
9842
|
+
}
|
|
9843
|
+
.media-row-btn.is-primary:hover {
|
|
9844
|
+
background: var(--color-button-primary-hover);
|
|
9845
|
+
}
|
|
9846
|
+
.media-custom-form {
|
|
9847
|
+
display: grid;
|
|
9848
|
+
grid-template-columns: max-content 1fr;
|
|
9849
|
+
gap: 0.4375rem 0.625rem;
|
|
9850
|
+
align-items: center;
|
|
9851
|
+
}
|
|
9852
|
+
.media-custom-form label {
|
|
9853
|
+
font-size: 0.75rem;
|
|
9854
|
+
color: var(--color-text-secondary);
|
|
9855
|
+
}
|
|
9856
|
+
.media-custom-form input {
|
|
9857
|
+
width: 100%;
|
|
9858
|
+
background: var(--color-bg-secondary);
|
|
9859
|
+
border: 1px solid var(--color-border-primary);
|
|
9860
|
+
color: var(--color-text-primary);
|
|
9861
|
+
border-radius: 6px;
|
|
9862
|
+
padding: 0.375rem 0.5rem;
|
|
9863
|
+
font-size: 0.8125rem;
|
|
9864
|
+
font-family: inherit;
|
|
9865
|
+
box-sizing: border-box;
|
|
9866
|
+
}
|
|
9867
|
+
.media-custom-form .media-form-actions {
|
|
9868
|
+
grid-column: 1 / -1;
|
|
9869
|
+
display: flex;
|
|
9870
|
+
gap: 0.5rem;
|
|
9871
|
+
justify-content: flex-end;
|
|
9872
|
+
margin-top: 0.25rem;
|
|
9873
|
+
}
|
data/lib/clacky/web/i18n.js
CHANGED
|
@@ -490,6 +490,35 @@ const I18n = (() => {
|
|
|
490
490
|
"settings.models.empty": "No models configured. Click \"+ Add Model\" to add one.",
|
|
491
491
|
"settings.models.badge.default": "Default",
|
|
492
492
|
"settings.models.badge.lite": "Lite",
|
|
493
|
+
"settings.media.title": "Media Generation",
|
|
494
|
+
"settings.media.desc": "Optional. Image / video / audio generation models.",
|
|
495
|
+
"settings.media.loading": "Loading…",
|
|
496
|
+
"settings.media.error": "Failed to load: {{msg}}",
|
|
497
|
+
"settings.media.kind.image": "Image",
|
|
498
|
+
"settings.media.kind.video": "Video",
|
|
499
|
+
"settings.media.kind.audio": "Audio",
|
|
500
|
+
"settings.media.source.off": "Off",
|
|
501
|
+
"settings.media.source.auto": "Auto",
|
|
502
|
+
"settings.media.source.custom": "Custom",
|
|
503
|
+
"settings.media.field.model": "Model",
|
|
504
|
+
"settings.media.field.baseUrl": "Base URL",
|
|
505
|
+
"settings.media.field.apiKey": "API Key",
|
|
506
|
+
"settings.media.field.provider":"Provider",
|
|
507
|
+
"settings.media.off.hint": "Disabled.",
|
|
508
|
+
"settings.media.auto.followsDefault": "Follows default chat model",
|
|
509
|
+
"settings.media.auto.noDefaultModel": "Set a default chat model first.",
|
|
510
|
+
"settings.media.auto.unsupported": "Current provider has no built-in model for this kind. Switch to Custom.",
|
|
511
|
+
"settings.media.auto.comingSoon": "Not available yet — no built-in providers.",
|
|
512
|
+
"settings.media.auto.disabledTitle": "No built-in provider available — use Custom.",
|
|
513
|
+
"settings.media.action.edit": "Edit",
|
|
514
|
+
"settings.media.action.save": "Save",
|
|
515
|
+
"settings.media.action.cancel": "Cancel",
|
|
516
|
+
"settings.media.action.saving": "Saving…",
|
|
517
|
+
"settings.media.action.saved": "Saved",
|
|
518
|
+
"settings.media.apiKey.placeholder": "Enter API key",
|
|
519
|
+
"settings.media.apiKey.required": "API key required",
|
|
520
|
+
"settings.media.model.required": "Model name required",
|
|
521
|
+
"settings.media.baseUrl.required": "Base URL required",
|
|
493
522
|
"settings.models.field.quicksetup": "Quick Setup",
|
|
494
523
|
"settings.models.field.model": "Model",
|
|
495
524
|
"settings.models.field.baseurl": "Base URL",
|
|
@@ -1180,6 +1209,35 @@ const I18n = (() => {
|
|
|
1180
1209
|
"settings.models.empty": "暂未配置模型,点击「+ 添加模型」添加。",
|
|
1181
1210
|
"settings.models.badge.default": "默认",
|
|
1182
1211
|
"settings.models.badge.lite": "轻量",
|
|
1212
|
+
"settings.media.title": "媒体生成",
|
|
1213
|
+
"settings.media.desc": "可选。图片 / 视频 / 音频 生成模型。",
|
|
1214
|
+
"settings.media.loading": "加载中…",
|
|
1215
|
+
"settings.media.error": "加载失败:{{msg}}",
|
|
1216
|
+
"settings.media.kind.image": "图片",
|
|
1217
|
+
"settings.media.kind.video": "视频",
|
|
1218
|
+
"settings.media.kind.audio": "音频",
|
|
1219
|
+
"settings.media.source.off": "关闭",
|
|
1220
|
+
"settings.media.source.auto": "自动",
|
|
1221
|
+
"settings.media.source.custom": "自定义",
|
|
1222
|
+
"settings.media.field.model": "模型",
|
|
1223
|
+
"settings.media.field.baseUrl": "Base URL",
|
|
1224
|
+
"settings.media.field.apiKey": "API Key",
|
|
1225
|
+
"settings.media.field.provider":"服务商",
|
|
1226
|
+
"settings.media.off.hint": "已关闭。",
|
|
1227
|
+
"settings.media.auto.followsDefault": "跟随默认聊天模型",
|
|
1228
|
+
"settings.media.auto.noDefaultModel": "请先设置默认聊天模型。",
|
|
1229
|
+
"settings.media.auto.unsupported": "当前服务商无内置模型,请切换到「自定义」。",
|
|
1230
|
+
"settings.media.auto.comingSoon": "暂无内置服务商,敬请期待。",
|
|
1231
|
+
"settings.media.auto.disabledTitle": "暂无内置服务商,请使用自定义。",
|
|
1232
|
+
"settings.media.action.edit": "编辑",
|
|
1233
|
+
"settings.media.action.save": "保存",
|
|
1234
|
+
"settings.media.action.cancel": "取消",
|
|
1235
|
+
"settings.media.action.saving": "保存中…",
|
|
1236
|
+
"settings.media.action.saved": "已保存",
|
|
1237
|
+
"settings.media.apiKey.placeholder": "请输入 API Key",
|
|
1238
|
+
"settings.media.apiKey.required": "请填写 API Key",
|
|
1239
|
+
"settings.media.model.required": "请填写模型名称",
|
|
1240
|
+
"settings.media.baseUrl.required": "请填写 Base URL",
|
|
1183
1241
|
"settings.models.field.quicksetup": "快速配置",
|
|
1184
1242
|
"settings.models.field.model": "Model",
|
|
1185
1243
|
"settings.models.field.baseurl": "Base URL",
|
data/lib/clacky/web/index.html
CHANGED
|
@@ -771,6 +771,17 @@
|
|
|
771
771
|
</div>
|
|
772
772
|
<div id="model-cards"></div>
|
|
773
773
|
</section>
|
|
774
|
+
|
|
775
|
+
<!-- Media generation section -->
|
|
776
|
+
<section class="settings-section" id="media-section">
|
|
777
|
+
<div class="settings-section-title">
|
|
778
|
+
<span data-i18n="settings.media.title">Media Generation</span>
|
|
779
|
+
</div>
|
|
780
|
+
<div class="settings-section-desc" data-i18n="settings.media.desc">
|
|
781
|
+
Generate images, video, and audio using your configured providers. "Auto" follows your default chat model.
|
|
782
|
+
</div>
|
|
783
|
+
<div id="media-rows"></div>
|
|
784
|
+
</section>
|
|
774
785
|
</div>
|
|
775
786
|
|
|
776
787
|
<!-- ══ Tab: UI ══ -->
|
|
@@ -1052,8 +1063,8 @@
|
|
|
1052
1063
|
<div id="model-modal-test-result" class="model-test-result"></div>
|
|
1053
1064
|
|
|
1054
1065
|
<label class="model-field model-field-checkbox" id="model-modal-default-field">
|
|
1055
|
-
<input type="checkbox" id="model-modal-set-default">
|
|
1056
|
-
<span data-i18n="settings.models.field.setDefault">Set as default model</span>
|
|
1066
|
+
<input type="checkbox" id="model-modal-set-default" class="field-checkbox">
|
|
1067
|
+
<span class="field-label" data-i18n="settings.models.field.setDefault">Set as default model</span>
|
|
1057
1068
|
</label>
|
|
1058
1069
|
</div>
|
|
1059
1070
|
<div class="modal-footer">
|
|
@@ -1288,6 +1299,7 @@
|
|
|
1288
1299
|
<script src="/skills.js"></script>
|
|
1289
1300
|
<script src="/channels.js"></script>
|
|
1290
1301
|
<script src="/mcp.js"></script>
|
|
1302
|
+
<script src="/model-tester.js"></script>
|
|
1291
1303
|
<script src="/settings.js"></script>
|
|
1292
1304
|
<script src="/billing.js"></script>
|
|
1293
1305
|
<script src="/onboard.js"></script>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Shared helpers for the model config UI flows.
|
|
2
|
+
// Used by both the onboarding wizard and the settings model modal.
|
|
3
|
+
window.ModelTester = (function () {
|
|
4
|
+
// Test a model connection.
|
|
5
|
+
// Returns one of:
|
|
6
|
+
// { ok: true, base_url, message } — connected, no rewrite
|
|
7
|
+
// { ok: true, base_url, message, rewrote: true } — connected, base_url auto-corrected (/v1 appended)
|
|
8
|
+
// { ok: false, message } — failed (server-reported or network)
|
|
9
|
+
async function testConnection({ model, base_url, api_key, anthropic_format, index } = {}) {
|
|
10
|
+
const body = { model, base_url, api_key };
|
|
11
|
+
if (typeof index === "number") body.index = index;
|
|
12
|
+
if (anthropic_format) body.anthropic_format = true;
|
|
13
|
+
|
|
14
|
+
let data;
|
|
15
|
+
try {
|
|
16
|
+
const res = await fetch("/api/config/test", {
|
|
17
|
+
method: "POST",
|
|
18
|
+
headers: { "Content-Type": "application/json" },
|
|
19
|
+
body: JSON.stringify(body)
|
|
20
|
+
});
|
|
21
|
+
data = await res.json();
|
|
22
|
+
} catch (e) {
|
|
23
|
+
return { ok: false, message: e.message };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!data.ok) return { ok: false, message: data.message || "" };
|
|
27
|
+
|
|
28
|
+
if (data.effective_base_url && data.effective_base_url !== base_url) {
|
|
29
|
+
return { ok: true, base_url: data.effective_base_url, message: data.message || "", rewrote: true };
|
|
30
|
+
}
|
|
31
|
+
return { ok: true, base_url, message: data.message || "" };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Persist a model config (create or update).
|
|
35
|
+
// existingId === null/undefined → POST /api/config/models (create).
|
|
36
|
+
// existingId === string → PATCH /api/config/models/:id (update).
|
|
37
|
+
// Returns { ok: bool, error? }.
|
|
38
|
+
async function saveModel(payload, { existingId } = {}) {
|
|
39
|
+
const url = existingId
|
|
40
|
+
? `/api/config/models/${encodeURIComponent(existingId)}`
|
|
41
|
+
: "/api/config/models";
|
|
42
|
+
const method = existingId ? "PATCH" : "POST";
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const res = await fetch(url, {
|
|
46
|
+
method,
|
|
47
|
+
headers: { "Content-Type": "application/json" },
|
|
48
|
+
body: JSON.stringify(payload)
|
|
49
|
+
});
|
|
50
|
+
const data = await res.json();
|
|
51
|
+
return data.ok ? { ok: true } : { ok: false, error: data.error || "" };
|
|
52
|
+
} catch (e) {
|
|
53
|
+
return { ok: false, error: e.message };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { testConnection, saveModel };
|
|
58
|
+
})();
|
data/lib/clacky/web/onboard.js
CHANGED
|
@@ -417,43 +417,30 @@ const Onboard = (() => {
|
|
|
417
417
|
_setResult(null, "");
|
|
418
418
|
|
|
419
419
|
// Step 1: test connection
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const data = await res.json();
|
|
427
|
-
if (!data.ok) {
|
|
428
|
-
_setResult(false, data.message || (zh ? "连接失败。" : "Connection failed."));
|
|
429
|
-
btn.disabled = false;
|
|
430
|
-
btn.textContent = I18n.t("onboard.key.btn.test");
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
|
-
} catch (e) {
|
|
434
|
-
_setResult(false, e.message);
|
|
420
|
+
const testResult = await ModelTester.testConnection({
|
|
421
|
+
model, base_url: baseUrl, api_key: apiKey, index: 0
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
if (!testResult.ok) {
|
|
425
|
+
_setResult(false, testResult.message || (zh ? "连接失败。" : "Connection failed."));
|
|
435
426
|
btn.disabled = false;
|
|
436
427
|
btn.textContent = I18n.t("onboard.key.btn.test");
|
|
437
428
|
return;
|
|
438
429
|
}
|
|
439
430
|
|
|
431
|
+
let effectiveBaseUrl = testResult.base_url;
|
|
432
|
+
if (testResult.rewrote) {
|
|
433
|
+
const baseInput = document.getElementById("setup-base-url");
|
|
434
|
+
if (baseInput) baseInput.value = effectiveBaseUrl;
|
|
435
|
+
}
|
|
436
|
+
|
|
440
437
|
// Step 2: save config
|
|
441
438
|
btn.textContent = I18n.t("onboard.key.saving");
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
});
|
|
448
|
-
const data = await res.json();
|
|
449
|
-
if (!data.ok) {
|
|
450
|
-
_setResult(false, data.error || (zh ? "保存失败。" : "Save failed."));
|
|
451
|
-
btn.disabled = false;
|
|
452
|
-
btn.textContent = I18n.t("onboard.key.btn.test");
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
} catch (e) {
|
|
456
|
-
_setResult(false, e.message);
|
|
439
|
+
const saveResult = await ModelTester.saveModel({
|
|
440
|
+
type: "default", model, base_url: effectiveBaseUrl, api_key: apiKey
|
|
441
|
+
});
|
|
442
|
+
if (!saveResult.ok) {
|
|
443
|
+
_setResult(false, saveResult.error || (zh ? "保存失败。" : "Save failed."));
|
|
457
444
|
btn.disabled = false;
|
|
458
445
|
btn.textContent = I18n.t("onboard.key.btn.test");
|
|
459
446
|
return;
|