@morlay/dsh-web-search-ollama 0.0.1-alpha.4 → 0.0.1-alpha.5
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.
- package/README.md +3 -3
- package/dist/client.cjs +72 -0
- package/dist/client.d.cts +35 -0
- package/dist/client.d.mts +35 -0
- package/dist/index.d.cts +81 -0
- package/dist/index.d.mts +18 -5
- package/dist/index.mjs +37 -12
- package/dist/rows.d.cts +22 -0
- package/dist/rows.d.mts +22 -0
- package/dist/rows.mjs +10 -0
- package/package.json +28 -10
- package/src/client/index.ts +52 -0
- package/src/client/locales.ts +27 -0
- package/src/index.ts +87 -24
- package/src/provider.ts +2 -2
- package/src/rows.ts +24 -0
- package/cordis.patch.yml +0 -11
package/README.md
CHANGED
|
@@ -73,8 +73,8 @@ mock 全绿替代它——这个包的第一版就是"配置指到 Ollama 的 me
|
|
|
73
73
|
|
|
74
74
|
## 装配
|
|
75
75
|
|
|
76
|
-
注册行由**本包的 bundle patch**([`
|
|
77
|
-
基础 bundle 的 `web` 行由 [`@morlay/dsh-profile`](../../
|
|
76
|
+
注册行由**本包的 bundle patch**([`src/rows.ts`](./src/rows.ts))插在 **host 层**;
|
|
77
|
+
基础 bundle 的 `web` 行由 [`@morlay/dsh-profile`](../../bundles/sandbox-profile/README.md) 的配置层切到
|
|
78
78
|
`ollama`——装配归本包、配置归 profile:
|
|
79
79
|
|
|
80
80
|
```yaml
|
|
@@ -98,4 +98,4 @@ mock 全绿替代它——这个包的第一版就是"配置指到 Ollama 的 me
|
|
|
98
98
|
- **一次只选一个后端**:`ctx.web` 的 `searchProvider` 是单选,默认 `ollama`;
|
|
99
99
|
`deepseek-official` 后端仍由基础 bundle 注册着,改写那一行即可切回(但它需要 `DEEPSEEK_API_KEY`)。
|
|
100
100
|
- **不做设置页 section**:配置面就是那一行 patch(见
|
|
101
|
-
[dsh-profile 的设计记录](../../
|
|
101
|
+
[dsh-profile 的设计记录](../../bundles/ollama-provider-profile/README.md))。
|
package/dist/client.cjs
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@morlay/dsh-web-search-ollama",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
//#region src/client/locales.ts
|
|
8
|
+
/** 本包在配置页上的字段文案(命名空间 `settings.web-search-ollama`)。 */
|
|
9
|
+
const zh = {
|
|
10
|
+
apiKey: "API key",
|
|
11
|
+
apiKeyHint: "字面值;能走凭证服务时优先用下面的引用名,别把密钥写进配置文件。",
|
|
12
|
+
apiKeyEnv: "凭证引用名",
|
|
13
|
+
apiKeyEnvHint: "每次搜索经凭证服务解析一次;默认 `OLLAMA_API_KEY`。",
|
|
14
|
+
baseURL: "端点",
|
|
15
|
+
baseURLHint: "端点根,`/api/web_search` 由 provider 拼;默认 https://ollama.com。",
|
|
16
|
+
maxResults: "默认结果数",
|
|
17
|
+
maxResultsHint: "请求没带结果数时用它;留空就是让 Ollama 用自己的默认。"
|
|
18
|
+
};
|
|
19
|
+
const en = {
|
|
20
|
+
apiKey: "API key",
|
|
21
|
+
apiKeyHint: "Literal value; prefer the credential reference below and never inline secrets.",
|
|
22
|
+
apiKeyEnv: "Credential reference",
|
|
23
|
+
apiKeyEnvHint: "Resolved through the credentials service per search; defaults to `OLLAMA_API_KEY`.",
|
|
24
|
+
baseURL: "Endpoint",
|
|
25
|
+
baseURLHint: "Endpoint root; the provider appends `/api/web_search`. Defaults to https://ollama.com.",
|
|
26
|
+
maxResults: "Default result count",
|
|
27
|
+
maxResultsHint: "Used when a request carries none; unset keeps Ollama's own default."
|
|
28
|
+
};
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/client/index.ts
|
|
31
|
+
/** 本包 host 行 id:行配置页读的就是这个命名空间。 */
|
|
32
|
+
const WEB_SEARCH_NS = "web-search-ollama";
|
|
33
|
+
/** 本页字典的命名空间。 */
|
|
34
|
+
const NS = "settings.web-search-ollama";
|
|
35
|
+
/** 认领的字段名。 */
|
|
36
|
+
const FIELDS = [
|
|
37
|
+
"apiKey",
|
|
38
|
+
"apiKeyEnv",
|
|
39
|
+
"baseURL",
|
|
40
|
+
"maxResults"
|
|
41
|
+
];
|
|
42
|
+
/** 需要的服务:槽位(字段槽)与字典。 */
|
|
43
|
+
const inject = ["locale"];
|
|
44
|
+
/**
|
|
45
|
+
* 给这一行的四个字段补文案。
|
|
46
|
+
* @param ctx - 浏览器插件上下文。
|
|
47
|
+
*/
|
|
48
|
+
function apply(ctx) {
|
|
49
|
+
const t = ctx.locale.bind(NS);
|
|
50
|
+
ctx.effect(() => ctx.locale.register(NS, {
|
|
51
|
+
zh,
|
|
52
|
+
en
|
|
53
|
+
}), "web-search-ollama: field locale");
|
|
54
|
+
ctx.inject(["schemaFormHints"], (scope) => scope.effect(() => {
|
|
55
|
+
const hints = scope.schemaFormHints;
|
|
56
|
+
const offs = FIELDS.map((key) => hints.describe(WEB_SEARCH_NS, [key], () => ({
|
|
57
|
+
label: t(key),
|
|
58
|
+
hint: t(`${key}Hint`)
|
|
59
|
+
})));
|
|
60
|
+
return () => {
|
|
61
|
+
for (const off of offs) off();
|
|
62
|
+
};
|
|
63
|
+
}, "web-search-ollama: field wording"));
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
exports.NS = NS;
|
|
67
|
+
exports.WEB_SEARCH_NS = WEB_SEARCH_NS;
|
|
68
|
+
exports.apply = apply;
|
|
69
|
+
exports.inject = inject;
|
|
70
|
+
return module.exports;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
//#region src/client/locales.d.ts
|
|
3
|
+
/** 本包在配置页上的字段文案(命名空间 `settings.web-search-ollama`)。 */
|
|
4
|
+
declare const zh: {
|
|
5
|
+
readonly apiKey: "API key";
|
|
6
|
+
readonly apiKeyHint: "字面值;能走凭证服务时优先用下面的引用名,别把密钥写进配置文件。";
|
|
7
|
+
readonly apiKeyEnv: "凭证引用名";
|
|
8
|
+
readonly apiKeyEnvHint: "每次搜索经凭证服务解析一次;默认 `OLLAMA_API_KEY`。";
|
|
9
|
+
readonly baseURL: "端点";
|
|
10
|
+
readonly baseURLHint: "端点根,`/api/web_search` 由 provider 拼;默认 https://ollama.com。";
|
|
11
|
+
readonly maxResults: "默认结果数";
|
|
12
|
+
readonly maxResultsHint: "请求没带结果数时用它;留空就是让 Ollama 用自己的默认。";
|
|
13
|
+
};
|
|
14
|
+
type WebSearchFieldLocaleKey = keyof typeof zh;
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/client/index.d.ts
|
|
17
|
+
/** 本包 host 行 id:行配置页读的就是这个命名空间。 */
|
|
18
|
+
declare const WEB_SEARCH_NS = "web-search-ollama";
|
|
19
|
+
/** 本页字典的命名空间。 */
|
|
20
|
+
declare const NS = "settings.web-search-ollama";
|
|
21
|
+
declare module "@deepseek-ai/dsh-client-ui-slots" {
|
|
22
|
+
interface LocaleNamespaceMap {
|
|
23
|
+
/** 设置页里这一行的字段文案。 */
|
|
24
|
+
"settings.web-search-ollama": WebSearchFieldLocaleKey;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** 需要的服务:槽位(字段槽)与字典。 */
|
|
28
|
+
declare const inject: string[];
|
|
29
|
+
/**
|
|
30
|
+
* 给这一行的四个字段补文案。
|
|
31
|
+
* @param ctx - 浏览器插件上下文。
|
|
32
|
+
*/
|
|
33
|
+
declare function apply(ctx: Context): void;
|
|
34
|
+
//#endregion
|
|
35
|
+
export { NS, WEB_SEARCH_NS, apply, inject };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
//#region src/client/locales.d.ts
|
|
3
|
+
/** 本包在配置页上的字段文案(命名空间 `settings.web-search-ollama`)。 */
|
|
4
|
+
declare const zh: {
|
|
5
|
+
readonly apiKey: "API key";
|
|
6
|
+
readonly apiKeyHint: "字面值;能走凭证服务时优先用下面的引用名,别把密钥写进配置文件。";
|
|
7
|
+
readonly apiKeyEnv: "凭证引用名";
|
|
8
|
+
readonly apiKeyEnvHint: "每次搜索经凭证服务解析一次;默认 `OLLAMA_API_KEY`。";
|
|
9
|
+
readonly baseURL: "端点";
|
|
10
|
+
readonly baseURLHint: "端点根,`/api/web_search` 由 provider 拼;默认 https://ollama.com。";
|
|
11
|
+
readonly maxResults: "默认结果数";
|
|
12
|
+
readonly maxResultsHint: "请求没带结果数时用它;留空就是让 Ollama 用自己的默认。";
|
|
13
|
+
};
|
|
14
|
+
type WebSearchFieldLocaleKey = keyof typeof zh;
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/client/index.d.ts
|
|
17
|
+
/** 本包 host 行 id:行配置页读的就是这个命名空间。 */
|
|
18
|
+
declare const WEB_SEARCH_NS = "web-search-ollama";
|
|
19
|
+
/** 本页字典的命名空间。 */
|
|
20
|
+
declare const NS = "settings.web-search-ollama";
|
|
21
|
+
declare module "@deepseek-ai/dsh-client-ui-slots" {
|
|
22
|
+
interface LocaleNamespaceMap {
|
|
23
|
+
/** 设置页里这一行的字段文案。 */
|
|
24
|
+
"settings.web-search-ollama": WebSearchFieldLocaleKey;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** 需要的服务:槽位(字段槽)与字典。 */
|
|
28
|
+
declare const inject: string[];
|
|
29
|
+
/**
|
|
30
|
+
* 给这一行的四个字段补文案。
|
|
31
|
+
* @param ctx - 浏览器插件上下文。
|
|
32
|
+
*/
|
|
33
|
+
declare function apply(ctx: Context): void;
|
|
34
|
+
//#endregion
|
|
35
|
+
export { NS, WEB_SEARCH_NS, apply, inject };
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
import { Volatile } from "@deepseek-ai/cosmokit";
|
|
3
|
+
import z from "@deepseek-ai/schemastery";
|
|
4
|
+
import { WebSearchProvider, WebSearchRequest, WebSearchResult, WebSearchSource } from "@deepseek-ai/dsh-web";
|
|
5
|
+
//#region src/types.d.ts
|
|
6
|
+
/** `results[]` 的一条;字段都可能缺失或为空串。 */
|
|
7
|
+
interface OllamaSearchResult {
|
|
8
|
+
title?: string | null;
|
|
9
|
+
url?: string | null;
|
|
10
|
+
content?: string | null;
|
|
11
|
+
}
|
|
12
|
+
/** 成功响应信封。 */
|
|
13
|
+
interface OllamaSearchResponse {
|
|
14
|
+
results?: OllamaSearchResult[];
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/provider.d.ts
|
|
18
|
+
/** 注册进 `ctx.web` 的 id;与 llm route 同名,配置里看到的就是同一个"ollama"。 */
|
|
19
|
+
declare const OLLAMA_PROVIDER_ID = "ollama";
|
|
20
|
+
/** 默认端点;`/api/web_search` 是操作。 */
|
|
21
|
+
declare const OLLAMA_DEFAULT_BASE_URL = "https://ollama.com";
|
|
22
|
+
/** Ollama 服务端自己的默认结果数。 */
|
|
23
|
+
declare const OLLAMA_DEFAULT_MAX_RESULTS = 5;
|
|
24
|
+
/** Ollama `max_results` 的硬上限;超过它的值不发给服务端。 */
|
|
25
|
+
declare const OLLAMA_MAX_RESULTS_LIMIT = 10;
|
|
26
|
+
interface OllamaSearchProviderOptions {
|
|
27
|
+
/** 字面 key;给了就压过 {@link resolveApiKey}。 */
|
|
28
|
+
apiKey?: string | undefined;
|
|
29
|
+
/** 运行期取 key 的入口(凭证服务 / 环境);返回空表示取不到。 */
|
|
30
|
+
resolveApiKey?: () => Promise<string | undefined>;
|
|
31
|
+
/** 取不到 key 时报出来的引用名,便于用户知道去哪配。 */
|
|
32
|
+
apiKeyEnv: string;
|
|
33
|
+
baseURL: string;
|
|
34
|
+
/** 请求没给 `maxResults` 时用的默认值;省略表示让 Ollama 用它自己的默认。 */
|
|
35
|
+
maxResults?: number | undefined;
|
|
36
|
+
}
|
|
37
|
+
/** 一条 Ollama 结果 → 一个 source;没有 url 就丢掉(seam 的 source 必须有 url)。 */
|
|
38
|
+
declare function mapOllamaResult(result: OllamaSearchResult): WebSearchSource | undefined;
|
|
39
|
+
/** 响应信封 → 归一化结果;形状不对就抛(由调用方包成 `WEB_PROVIDER_ERROR`)。 */
|
|
40
|
+
declare function mapOllamaResponse(response: OllamaSearchResponse): WebSearchResult;
|
|
41
|
+
/** Ollama 的搜索后端;HTTP 重定向按 `WEB_PROVIDER_ERROR` 失败。 */
|
|
42
|
+
declare class OllamaSearchProvider implements WebSearchProvider {
|
|
43
|
+
private readonly options;
|
|
44
|
+
readonly id = "ollama";
|
|
45
|
+
constructor(options: OllamaSearchProviderOptions);
|
|
46
|
+
available(): boolean;
|
|
47
|
+
search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult>;
|
|
48
|
+
/** 一次操作一份 key,不留在 provider 上;没有可用的 key 就点名引用,而不是发无授权请求。 */
|
|
49
|
+
private apiKey;
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/index.d.ts
|
|
53
|
+
declare const name = "web-search-ollama";
|
|
54
|
+
/** 只往 `ctx.web` 注册后端,不发布服务。 */
|
|
55
|
+
declare const inject: string[];
|
|
56
|
+
interface Config {
|
|
57
|
+
/** 字面 key;优先用 `apiKeyEnv` 走凭证服务,别把密钥写进配置文件。 */
|
|
58
|
+
apiKey?: string;
|
|
59
|
+
/** 每次搜索解析一次的凭证引用;缺省 `OLLAMA_API_KEY`。 */
|
|
60
|
+
apiKeyEnv?: string;
|
|
61
|
+
/** 端点根;`/api/web_search` 由 provider 拼。缺省 ollama.com。 */
|
|
62
|
+
baseURL?: string;
|
|
63
|
+
/** 请求没带 `maxResults` 时的默认结果数;省略表示让 Ollama 用自己的默认。 */
|
|
64
|
+
maxResults?: number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* schema 解析之后的形状:四个字段都是 **volatile 稳定引用**,读它要过 `.get()`。
|
|
68
|
+
*
|
|
69
|
+
* 它们都标了 `.volatile()`(插件行里唯一可实时改的字段),所以设置页改完不用重挂这一行:provider 的每一次
|
|
70
|
+
* 请求、每一次取 key 都现场读引用。
|
|
71
|
+
*/
|
|
72
|
+
interface ResolvedConfig {
|
|
73
|
+
readonly apiKey: Volatile<string | undefined>;
|
|
74
|
+
readonly apiKeyEnv: Volatile<string | undefined>;
|
|
75
|
+
readonly baseURL: Volatile<string | undefined>;
|
|
76
|
+
readonly maxResults: Volatile<number | undefined>;
|
|
77
|
+
}
|
|
78
|
+
declare const Config: z<Config, ResolvedConfig>;
|
|
79
|
+
declare function apply(ctx: Context, config: ResolvedConfig): void;
|
|
80
|
+
//#endregion
|
|
81
|
+
export { Config, OLLAMA_DEFAULT_BASE_URL, OLLAMA_DEFAULT_MAX_RESULTS, OLLAMA_MAX_RESULTS_LIMIT, OLLAMA_PROVIDER_ID, OllamaSearchProvider, type OllamaSearchProviderOptions, ResolvedConfig, apply, inject, mapOllamaResponse, mapOllamaResult, name };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
2
|
import { WebSearchProvider, WebSearchRequest, WebSearchResult, WebSearchSource } from "@deepseek-ai/dsh-web";
|
|
3
3
|
import { Context } from "@deepseek-ai/cordis";
|
|
4
|
+
import { Volatile } from "@deepseek-ai/cosmokit";
|
|
4
5
|
//#region src/types.d.ts
|
|
5
6
|
/** `results[]` 的一条;字段都可能缺失或为空串。 */
|
|
6
7
|
interface OllamaSearchResult {
|
|
@@ -24,14 +25,14 @@ declare const OLLAMA_DEFAULT_MAX_RESULTS = 5;
|
|
|
24
25
|
declare const OLLAMA_MAX_RESULTS_LIMIT = 10;
|
|
25
26
|
interface OllamaSearchProviderOptions {
|
|
26
27
|
/** 字面 key;给了就压过 {@link resolveApiKey}。 */
|
|
27
|
-
apiKey?: string;
|
|
28
|
+
apiKey?: string | undefined;
|
|
28
29
|
/** 运行期取 key 的入口(凭证服务 / 环境);返回空表示取不到。 */
|
|
29
30
|
resolveApiKey?: () => Promise<string | undefined>;
|
|
30
31
|
/** 取不到 key 时报出来的引用名,便于用户知道去哪配。 */
|
|
31
32
|
apiKeyEnv: string;
|
|
32
33
|
baseURL: string;
|
|
33
34
|
/** 请求没给 `maxResults` 时用的默认值;省略表示让 Ollama 用它自己的默认。 */
|
|
34
|
-
maxResults?: number;
|
|
35
|
+
maxResults?: number | undefined;
|
|
35
36
|
}
|
|
36
37
|
/** 一条 Ollama 结果 → 一个 source;没有 url 就丢掉(seam 的 source 必须有 url)。 */
|
|
37
38
|
declare function mapOllamaResult(result: OllamaSearchResult): WebSearchSource | undefined;
|
|
@@ -62,7 +63,19 @@ interface Config {
|
|
|
62
63
|
/** 请求没带 `maxResults` 时的默认结果数;省略表示让 Ollama 用自己的默认。 */
|
|
63
64
|
maxResults?: number;
|
|
64
65
|
}
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
/**
|
|
67
|
+
* schema 解析之后的形状:四个字段都是 **volatile 稳定引用**,读它要过 `.get()`。
|
|
68
|
+
*
|
|
69
|
+
* 它们都标了 `.volatile()`(插件行里唯一可实时改的字段),所以设置页改完不用重挂这一行:provider 的每一次
|
|
70
|
+
* 请求、每一次取 key 都现场读引用。
|
|
71
|
+
*/
|
|
72
|
+
interface ResolvedConfig {
|
|
73
|
+
readonly apiKey: Volatile<string | undefined>;
|
|
74
|
+
readonly apiKeyEnv: Volatile<string | undefined>;
|
|
75
|
+
readonly baseURL: Volatile<string | undefined>;
|
|
76
|
+
readonly maxResults: Volatile<number | undefined>;
|
|
77
|
+
}
|
|
78
|
+
declare const Config: z<Config, ResolvedConfig>;
|
|
79
|
+
declare function apply(ctx: Context, config: ResolvedConfig): void;
|
|
67
80
|
//#endregion
|
|
68
|
-
export { Config, OLLAMA_DEFAULT_BASE_URL, OLLAMA_DEFAULT_MAX_RESULTS, OLLAMA_MAX_RESULTS_LIMIT, OLLAMA_PROVIDER_ID, OllamaSearchProvider, type OllamaSearchProviderOptions, apply, inject, mapOllamaResponse, mapOllamaResult, name };
|
|
81
|
+
export { Config, OLLAMA_DEFAULT_BASE_URL, OLLAMA_DEFAULT_MAX_RESULTS, OLLAMA_MAX_RESULTS_LIMIT, OLLAMA_PROVIDER_ID, OllamaSearchProvider, type OllamaSearchProviderOptions, ResolvedConfig, apply, inject, mapOllamaResponse, mapOllamaResult, name };
|
package/dist/index.mjs
CHANGED
|
@@ -121,26 +121,51 @@ const name = "web-search-ollama";
|
|
|
121
121
|
const inject = ["web"];
|
|
122
122
|
/** 与 `llm-pi-ai` 的 ollama route 共用同一个 key 引用。 */
|
|
123
123
|
const DEFAULT_API_KEY_ENV = "OLLAMA_API_KEY";
|
|
124
|
+
/**
|
|
125
|
+
* 本地化说明:`description()` 的类型签名只声明 `string`,而 meta 本身接受 `Dict<string>`
|
|
126
|
+
* (`vendor/schemastery/src/index.ts` 的 `mergeDesc` 就是按字典合并的),所以这里只做一次类型放行。
|
|
127
|
+
*/
|
|
128
|
+
const localized = (text) => text;
|
|
124
129
|
const Config = z.object({
|
|
125
|
-
apiKey: z.string().role("secret")
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
130
|
+
apiKey: z.string().role("secret").description(localized({
|
|
131
|
+
zh: "Ollama 的 API key 字面值;能用凭证服务时优先用下面的引用名,别把密钥写进配置文件。",
|
|
132
|
+
en: "Literal Ollama API key. Prefer the credential reference below; do not put secrets in a config file."
|
|
133
|
+
})).volatile(),
|
|
134
|
+
apiKeyEnv: z.string().role("credential-ref").description(localized({
|
|
135
|
+
zh: "凭证引用名(默认 `OLLAMA_API_KEY`):每次搜索经凭证服务解析一次。",
|
|
136
|
+
en: "Credential reference resolved through the credentials service on every search (default `OLLAMA_API_KEY`)."
|
|
137
|
+
})).volatile(),
|
|
138
|
+
baseURL: z.string().description(localized({
|
|
139
|
+
zh: "端点根,`/api/web_search` 由 provider 拼;默认 https://ollama.com。",
|
|
140
|
+
en: "Endpoint root; the provider appends `/api/web_search`. Defaults to https://ollama.com."
|
|
141
|
+
})).volatile(),
|
|
142
|
+
maxResults: z.number().step(1).min(1).description(localized({
|
|
143
|
+
zh: "请求没带结果数时用的默认值;省略就是让 Ollama 用自己的默认。",
|
|
144
|
+
en: "Default result count when a request carries none; unset keeps Ollama's own default."
|
|
145
|
+
})).volatile()
|
|
129
146
|
});
|
|
130
147
|
function apply(ctx, config) {
|
|
131
|
-
const apiKeyEnv = credentialRef(config.apiKeyEnv ?? DEFAULT_API_KEY_ENV);
|
|
132
|
-
const literal = config.apiKey !== void 0 && config.apiKey.length > 0 ? config.apiKey : void 0;
|
|
133
148
|
ctx.web.registerSearchProvider(new OllamaSearchProvider({
|
|
134
|
-
|
|
149
|
+
get apiKey() {
|
|
150
|
+
const literal = config.apiKey.get();
|
|
151
|
+
return literal !== void 0 && literal.length > 0 ? literal : void 0;
|
|
152
|
+
},
|
|
135
153
|
resolveApiKey: async () => {
|
|
154
|
+
const reference = credentialRef(config.apiKeyEnv.get() ?? DEFAULT_API_KEY_ENV);
|
|
136
155
|
const credentials = ctx.get("credentials");
|
|
137
|
-
if (credentials !== void 0) return (await credentials.resolve(
|
|
138
|
-
const ambient = launchEnvironmentOf(ctx).get(
|
|
156
|
+
if (credentials !== void 0) return (await credentials.resolve(reference))?.value;
|
|
157
|
+
const ambient = launchEnvironmentOf(ctx).get(reference);
|
|
139
158
|
return ambient !== void 0 && ambient.value.length > 0 ? ambient.value : void 0;
|
|
140
159
|
},
|
|
141
|
-
apiKeyEnv
|
|
142
|
-
|
|
143
|
-
|
|
160
|
+
get apiKeyEnv() {
|
|
161
|
+
return config.apiKeyEnv.get() ?? DEFAULT_API_KEY_ENV;
|
|
162
|
+
},
|
|
163
|
+
get baseURL() {
|
|
164
|
+
return config.baseURL.get() ?? "https://ollama.com";
|
|
165
|
+
},
|
|
166
|
+
get maxResults() {
|
|
167
|
+
return config.maxResults.get();
|
|
168
|
+
}
|
|
144
169
|
}));
|
|
145
170
|
}
|
|
146
171
|
//#endregion
|
package/dist/rows.d.cts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/rows.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* 本包作为能力包发布的**装配数据**:行本身在部署那一层装
|
|
4
|
+
* (`packages/bundles/ollama-provider-profile` 引用它渲染 patch;那里同时给 `web.searchProvider` 与 key 引用)。
|
|
5
|
+
*
|
|
6
|
+
* 注册行必须在 host 平面只装一次——同一个 provider id 注册两份会撞 `WEB_DUPLICATE_PROVIDER`。
|
|
7
|
+
*/
|
|
8
|
+
/** 一行装配条目:与 `cordis.patch.yml` 的顶层结构同形。 */
|
|
9
|
+
interface PatchRow {
|
|
10
|
+
readonly id?: string;
|
|
11
|
+
readonly insert?: readonly RowEntry[];
|
|
12
|
+
}
|
|
13
|
+
/** `insert` 里的一个条目。 */
|
|
14
|
+
interface RowEntry {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly config?: Readonly<Record<string, unknown>>;
|
|
18
|
+
}
|
|
19
|
+
/** 搜索后端的注册行(config 的值由部署那层按 id 覆盖)。 */
|
|
20
|
+
declare function webSearchRows(): readonly PatchRow[];
|
|
21
|
+
//#endregion
|
|
22
|
+
export { PatchRow, RowEntry, webSearchRows };
|
package/dist/rows.d.mts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/rows.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* 本包作为能力包发布的**装配数据**:行本身在部署那一层装
|
|
4
|
+
* (`packages/bundles/ollama-provider-profile` 引用它渲染 patch;那里同时给 `web.searchProvider` 与 key 引用)。
|
|
5
|
+
*
|
|
6
|
+
* 注册行必须在 host 平面只装一次——同一个 provider id 注册两份会撞 `WEB_DUPLICATE_PROVIDER`。
|
|
7
|
+
*/
|
|
8
|
+
/** 一行装配条目:与 `cordis.patch.yml` 的顶层结构同形。 */
|
|
9
|
+
interface PatchRow {
|
|
10
|
+
readonly id?: string;
|
|
11
|
+
readonly insert?: readonly RowEntry[];
|
|
12
|
+
}
|
|
13
|
+
/** `insert` 里的一个条目。 */
|
|
14
|
+
interface RowEntry {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly config?: Readonly<Record<string, unknown>>;
|
|
18
|
+
}
|
|
19
|
+
/** 搜索后端的注册行(config 的值由部署那层按 id 覆盖)。 */
|
|
20
|
+
declare function webSearchRows(): readonly PatchRow[];
|
|
21
|
+
//#endregion
|
|
22
|
+
export { PatchRow, RowEntry, webSearchRows };
|
package/dist/rows.mjs
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morlay/dsh-web-search-ollama",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.5",
|
|
4
4
|
"description": "Ollama-backed search provider for the DeepSeek Harness web seam (ctx.web): one web_search backend calling POST https://ollama.com/api/web_search.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
|
@@ -17,28 +17,46 @@
|
|
|
17
17
|
"locale/*.json",
|
|
18
18
|
"dist",
|
|
19
19
|
"src",
|
|
20
|
-
"cordis.patch.yml",
|
|
21
20
|
"!**/__tests__"
|
|
22
21
|
],
|
|
23
22
|
"type": "module",
|
|
24
23
|
"exports": {
|
|
25
24
|
".": "./dist/index.mjs",
|
|
25
|
+
"./rows": "./dist/rows.mjs",
|
|
26
26
|
"./package.json": "./package.json",
|
|
27
|
-
"./
|
|
27
|
+
"./client": {
|
|
28
|
+
"types": "./dist/client.d.cts",
|
|
29
|
+
"default": "./dist/client.cjs"
|
|
30
|
+
},
|
|
28
31
|
"./locale/*.json": "./locale/*.json"
|
|
29
32
|
},
|
|
30
33
|
"dependencies": {
|
|
31
|
-
"@deepseek-ai/schemastery": "3.18.4"
|
|
34
|
+
"@deepseek-ai/schemastery": "^3.18.4"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@deepseek-ai/cosmokit": "1.8.5",
|
|
38
|
+
"@deepseek-ai/dsh-client-locale": "0.1.7-rc.2",
|
|
39
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.2",
|
|
40
|
+
"@morlay/dsh-client-ui-primitives": "0.0.2-alpha.9"
|
|
32
41
|
},
|
|
33
42
|
"peerDependencies": {
|
|
34
|
-
"@deepseek-ai/cordis": "4.0.4",
|
|
35
|
-
"@deepseek-ai/
|
|
36
|
-
"@deepseek-ai/dsh-
|
|
37
|
-
"@deepseek-ai/dsh-
|
|
43
|
+
"@deepseek-ai/cordis": "^4.0.4",
|
|
44
|
+
"@deepseek-ai/cosmokit": "^1.8.5",
|
|
45
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.7-rc.2",
|
|
46
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.7-rc.2",
|
|
47
|
+
"@deepseek-ai/dsh-credentials": "^0.1.7-rc.2",
|
|
48
|
+
"@deepseek-ai/dsh-launch-environment": "^0.1.7-rc.2",
|
|
49
|
+
"@deepseek-ai/dsh-web": "^0.1.7-rc.2",
|
|
50
|
+
"@morlay/dsh-client-ui-primitives": "0.0.2-alpha.9"
|
|
38
51
|
},
|
|
39
52
|
"dsh": {
|
|
40
|
-
"
|
|
41
|
-
"
|
|
53
|
+
"client": {
|
|
54
|
+
"platform": "web",
|
|
55
|
+
"inject": [
|
|
56
|
+
"@deepseek-ai/dsh-client-locale",
|
|
57
|
+
"@deepseek-ai/dsh-client-ui-slots",
|
|
58
|
+
"@morlay/dsh-client-ui-primitives"
|
|
59
|
+
]
|
|
42
60
|
}
|
|
43
61
|
},
|
|
44
62
|
"scripts": {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* client 半:这一行的配置页由通用 schema 表单按 volatile 字段生成(四个字段都能在页面上改,改完下一次请求就
|
|
3
|
+
* 生效),本包只把英文键名换成中文标签与说明。
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
7
|
+
import type {} from "@deepseek-ai/dsh-client-locale/client";
|
|
8
|
+
import type {} from "@deepseek-ai/dsh-client-ui-slots";
|
|
9
|
+
import { en, zh, type WebSearchFieldLocaleKey } from "./locales.ts";
|
|
10
|
+
|
|
11
|
+
/** 本包 host 行 id:行配置页读的就是这个命名空间。 */
|
|
12
|
+
export const WEB_SEARCH_NS = "web-search-ollama";
|
|
13
|
+
|
|
14
|
+
/** 本页字典的命名空间。 */
|
|
15
|
+
export const NS = "settings.web-search-ollama";
|
|
16
|
+
|
|
17
|
+
/** 认领的字段名。 */
|
|
18
|
+
const FIELDS = ["apiKey", "apiKeyEnv", "baseURL", "maxResults"] as const;
|
|
19
|
+
|
|
20
|
+
declare module "@deepseek-ai/dsh-client-ui-slots" {
|
|
21
|
+
interface LocaleNamespaceMap {
|
|
22
|
+
/** 设置页里这一行的字段文案。 */
|
|
23
|
+
"settings.web-search-ollama": WebSearchFieldLocaleKey;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** 需要的服务:槽位(字段槽)与字典。 */
|
|
28
|
+
export const inject = ["locale"];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 给这一行的四个字段补文案。
|
|
32
|
+
* @param ctx - 浏览器插件上下文。
|
|
33
|
+
*/
|
|
34
|
+
export function apply(ctx: Context): void {
|
|
35
|
+
const t = ctx.locale.bind(NS);
|
|
36
|
+
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "web-search-ollama: field locale");
|
|
37
|
+
// 提示面由行配置表单提供:等它可用再注册(`ctx.get` 在它还没提供时拿不到,注册会被静静跳过)。
|
|
38
|
+
ctx.inject(["schemaFormHints"], (scope) =>
|
|
39
|
+
scope.effect(() => {
|
|
40
|
+
const hints = scope.schemaFormHints;
|
|
41
|
+
const offs = FIELDS.map((key) =>
|
|
42
|
+
hints.describe(WEB_SEARCH_NS, [key], () => ({
|
|
43
|
+
label: t(key),
|
|
44
|
+
hint: t(`${key}Hint` as WebSearchFieldLocaleKey),
|
|
45
|
+
})),
|
|
46
|
+
);
|
|
47
|
+
return () => {
|
|
48
|
+
for (const off of offs) off();
|
|
49
|
+
};
|
|
50
|
+
}, "web-search-ollama: field wording"),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** 本包在配置页上的字段文案(命名空间 `settings.web-search-ollama`)。 */
|
|
2
|
+
|
|
3
|
+
export const zh = {
|
|
4
|
+
apiKey: "API key",
|
|
5
|
+
apiKeyHint: "字面值;能走凭证服务时优先用下面的引用名,别把密钥写进配置文件。",
|
|
6
|
+
apiKeyEnv: "凭证引用名",
|
|
7
|
+
apiKeyEnvHint: "每次搜索经凭证服务解析一次;默认 `OLLAMA_API_KEY`。",
|
|
8
|
+
baseURL: "端点",
|
|
9
|
+
baseURLHint: "端点根,`/api/web_search` 由 provider 拼;默认 https://ollama.com。",
|
|
10
|
+
maxResults: "默认结果数",
|
|
11
|
+
maxResultsHint: "请求没带结果数时用它;留空就是让 Ollama 用自己的默认。",
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
14
|
+
export const en: Record<keyof typeof zh, string> = {
|
|
15
|
+
apiKey: "API key",
|
|
16
|
+
apiKeyHint: "Literal value; prefer the credential reference below and never inline secrets.",
|
|
17
|
+
apiKeyEnv: "Credential reference",
|
|
18
|
+
apiKeyEnvHint:
|
|
19
|
+
"Resolved through the credentials service per search; defaults to `OLLAMA_API_KEY`.",
|
|
20
|
+
baseURL: "Endpoint",
|
|
21
|
+
baseURLHint:
|
|
22
|
+
"Endpoint root; the provider appends `/api/web_search`. Defaults to https://ollama.com.",
|
|
23
|
+
maxResults: "Default result count",
|
|
24
|
+
maxResultsHint: "Used when a request carries none; unset keeps Ollama's own default.",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type WebSearchFieldLocaleKey = keyof typeof zh;
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Volatile } from "@deepseek-ai/cosmokit";
|
|
1
2
|
import type { Context } from "@deepseek-ai/cordis";
|
|
2
3
|
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
3
4
|
import { launchEnvironmentOf } from "@deepseek-ai/dsh-launch-environment";
|
|
@@ -35,30 +36,92 @@ export interface Config {
|
|
|
35
36
|
maxResults?: number;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
/**
|
|
40
|
+
* schema 解析之后的形状:四个字段都是 **volatile 稳定引用**,读它要过 `.get()`。
|
|
41
|
+
*
|
|
42
|
+
* 它们都标了 `.volatile()`(插件行里唯一可实时改的字段),所以设置页改完不用重挂这一行:provider 的每一次
|
|
43
|
+
* 请求、每一次取 key 都现场读引用。
|
|
44
|
+
*/
|
|
45
|
+
export interface ResolvedConfig {
|
|
46
|
+
readonly apiKey: Volatile<string | undefined>;
|
|
47
|
+
readonly apiKeyEnv: Volatile<string | undefined>;
|
|
48
|
+
readonly baseURL: Volatile<string | undefined>;
|
|
49
|
+
readonly maxResults: Volatile<number | undefined>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 本地化说明:`description()` 的类型签名只声明 `string`,而 meta 本身接受 `Dict<string>`
|
|
54
|
+
* (`vendor/schemastery/src/index.ts` 的 `mergeDesc` 就是按字典合并的),所以这里只做一次类型放行。
|
|
55
|
+
*/
|
|
56
|
+
const localized = (text: { zh: string; en: string }): string => text as unknown as string;
|
|
57
|
+
|
|
58
|
+
export const Config: z<Config, ResolvedConfig> = z.object({
|
|
59
|
+
apiKey: z
|
|
60
|
+
.string()
|
|
61
|
+
.role("secret")
|
|
62
|
+
.description(
|
|
63
|
+
localized({
|
|
64
|
+
zh: "Ollama 的 API key 字面值;能用凭证服务时优先用下面的引用名,别把密钥写进配置文件。",
|
|
65
|
+
en: "Literal Ollama API key. Prefer the credential reference below; do not put secrets in a config file.",
|
|
66
|
+
}),
|
|
67
|
+
)
|
|
68
|
+
.volatile(),
|
|
69
|
+
apiKeyEnv: z
|
|
70
|
+
.string()
|
|
71
|
+
.role("credential-ref")
|
|
72
|
+
.description(
|
|
73
|
+
localized({
|
|
74
|
+
zh: "凭证引用名(默认 `OLLAMA_API_KEY`):每次搜索经凭证服务解析一次。",
|
|
75
|
+
en: "Credential reference resolved through the credentials service on every search (default `OLLAMA_API_KEY`).",
|
|
76
|
+
}),
|
|
77
|
+
)
|
|
78
|
+
.volatile(),
|
|
79
|
+
baseURL: z
|
|
80
|
+
.string()
|
|
81
|
+
.description(
|
|
82
|
+
localized({
|
|
83
|
+
zh: "端点根,`/api/web_search` 由 provider 拼;默认 https://ollama.com。",
|
|
84
|
+
en: "Endpoint root; the provider appends `/api/web_search`. Defaults to https://ollama.com.",
|
|
85
|
+
}),
|
|
86
|
+
)
|
|
87
|
+
.volatile(),
|
|
88
|
+
maxResults: z
|
|
89
|
+
.number()
|
|
90
|
+
.step(1)
|
|
91
|
+
.min(1)
|
|
92
|
+
.description(
|
|
93
|
+
localized({
|
|
94
|
+
zh: "请求没带结果数时用的默认值;省略就是让 Ollama 用自己的默认。",
|
|
95
|
+
en: "Default result count when a request carries none; unset keeps Ollama's own default.",
|
|
96
|
+
}),
|
|
97
|
+
)
|
|
98
|
+
.volatile(),
|
|
43
99
|
});
|
|
44
100
|
|
|
45
|
-
export function apply(ctx: Context, config:
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
101
|
+
export function apply(ctx: Context, config: ResolvedConfig): void {
|
|
102
|
+
// provider 每次都从 options 读,所以这里用 getter 把「现场读引用」接上去:改配置即下一次请求生效。
|
|
103
|
+
const options = {
|
|
104
|
+
get apiKey(): string | undefined {
|
|
105
|
+
const literal = config.apiKey.get();
|
|
106
|
+
return literal !== undefined && literal.length > 0 ? literal : undefined;
|
|
107
|
+
},
|
|
108
|
+
resolveApiKey: async () => {
|
|
109
|
+
const reference = credentialRef(config.apiKeyEnv.get() ?? DEFAULT_API_KEY_ENV);
|
|
110
|
+
const credentials = ctx.get("credentials");
|
|
111
|
+
if (credentials !== undefined) return (await credentials.resolve(reference))?.value;
|
|
112
|
+
// 没有凭证服务时环境就是整个凭证面。
|
|
113
|
+
const ambient = launchEnvironmentOf(ctx).get(reference);
|
|
114
|
+
return ambient !== undefined && ambient.value.length > 0 ? ambient.value : undefined;
|
|
115
|
+
},
|
|
116
|
+
get apiKeyEnv(): string {
|
|
117
|
+
return config.apiKeyEnv.get() ?? DEFAULT_API_KEY_ENV;
|
|
118
|
+
},
|
|
119
|
+
get baseURL(): string {
|
|
120
|
+
return config.baseURL.get() ?? OLLAMA_DEFAULT_BASE_URL;
|
|
121
|
+
},
|
|
122
|
+
get maxResults(): number | undefined {
|
|
123
|
+
return config.maxResults.get();
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
ctx.web.registerSearchProvider(new OllamaSearchProvider(options));
|
|
64
127
|
}
|
package/src/provider.ts
CHANGED
|
@@ -24,14 +24,14 @@ const USER_AGENT = "morlay-dsh-plugin/0.0.1";
|
|
|
24
24
|
|
|
25
25
|
export interface OllamaSearchProviderOptions {
|
|
26
26
|
/** 字面 key;给了就压过 {@link resolveApiKey}。 */
|
|
27
|
-
apiKey?: string;
|
|
27
|
+
apiKey?: string | undefined;
|
|
28
28
|
/** 运行期取 key 的入口(凭证服务 / 环境);返回空表示取不到。 */
|
|
29
29
|
resolveApiKey?: () => Promise<string | undefined>;
|
|
30
30
|
/** 取不到 key 时报出来的引用名,便于用户知道去哪配。 */
|
|
31
31
|
apiKeyEnv: string;
|
|
32
32
|
baseURL: string;
|
|
33
33
|
/** 请求没给 `maxResults` 时用的默认值;省略表示让 Ollama 用它自己的默认。 */
|
|
34
|
-
maxResults?: number;
|
|
34
|
+
maxResults?: number | undefined;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/** 一条 Ollama 结果 → 一个 source;没有 url 就丢掉(seam 的 source 必须有 url)。 */
|
package/src/rows.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 本包作为能力包发布的**装配数据**:行本身在部署那一层装
|
|
3
|
+
* (`packages/bundles/ollama-provider-profile` 引用它渲染 patch;那里同时给 `web.searchProvider` 与 key 引用)。
|
|
4
|
+
*
|
|
5
|
+
* 注册行必须在 host 平面只装一次——同一个 provider id 注册两份会撞 `WEB_DUPLICATE_PROVIDER`。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** 一行装配条目:与 `cordis.patch.yml` 的顶层结构同形。 */
|
|
9
|
+
export interface PatchRow {
|
|
10
|
+
readonly id?: string;
|
|
11
|
+
readonly insert?: readonly RowEntry[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** `insert` 里的一个条目。 */
|
|
15
|
+
export interface RowEntry {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly config?: Readonly<Record<string, unknown>>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** 搜索后端的注册行(config 的值由部署那层按 id 覆盖)。 */
|
|
22
|
+
export function webSearchRows(): readonly PatchRow[] {
|
|
23
|
+
return [{ insert: [{ id: "web-search-ollama", name: "@morlay/dsh-web-search-ollama" }] }];
|
|
24
|
+
}
|
package/cordis.patch.yml
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# @morlay/dsh-web-search-ollama bundle patch:把搜索后端注册到 host 层。
|
|
2
|
-
#
|
|
3
|
-
# 为什么住 host:注册行在 profile 平面只装一次——同一个 provider id 注册两份会撞
|
|
4
|
-
# `WEB_DUPLICATE_PROVIDER`;至于选它还是别的后端,由 `web` 行的
|
|
5
|
-
# `searchProvider` 决定——那是配置,归 `@morlay/dsh-profile`。两者分工:这里插行,那边配值。
|
|
6
|
-
#
|
|
7
|
-
# 行不带 config:`apiKeyEnv` 这类值同样由 `@morlay/dsh-profile` 做 config 覆盖。
|
|
8
|
-
|
|
9
|
-
- insert:
|
|
10
|
-
- id: web-search-ollama
|
|
11
|
-
name: "@morlay/dsh-web-search-ollama"
|