@oh-my-pi/pi-ai 16.1.13 → 16.1.14
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/CHANGELOG.md +19 -0
- package/dist/types/providers/aws-credentials.d.ts +2 -0
- package/dist/types/registry/huggingface.d.ts +2 -2
- package/dist/types/registry/oauth/minimax-code.d.ts +2 -3
- package/dist/types/registry/qianfan.d.ts +2 -2
- package/dist/types/registry/registry.d.ts +4 -0
- package/dist/types/registry/sakana.d.ts +7 -0
- package/dist/types/registry/venice.d.ts +2 -2
- package/dist/types/registry/zai.d.ts +2 -2
- package/dist/types/registry/zhipu-coding-plan.d.ts +2 -2
- package/dist/types/utils/deterministic-id.d.ts +16 -0
- package/dist/types/utils/proxy.d.ts +29 -0
- package/package.json +4 -4
- package/src/auth-gateway/server.ts +4 -5
- package/src/providers/amazon-bedrock.ts +1 -0
- package/src/providers/aws-credentials.ts +23 -10
- package/src/providers/cursor.ts +12 -15
- package/src/providers/devin.ts +7 -14
- package/src/providers/openai-responses.ts +2 -1
- package/src/providers/openai-shared.ts +17 -0
- package/src/registry/huggingface.ts +13 -35
- package/src/registry/oauth/minimax-code.ts +19 -48
- package/src/registry/qianfan.ts +12 -34
- package/src/registry/registry.ts +2 -0
- package/src/registry/sakana.ts +22 -0
- package/src/registry/venice.ts +12 -34
- package/src/registry/zai.ts +12 -33
- package/src/registry/zhipu-coding-plan.ts +12 -33
- package/src/stream.ts +18 -13
- package/src/utils/deterministic-id.ts +20 -0
- package/src/utils/proxy.ts +239 -0
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
* China: https://api.minimaxi.com/v1
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import {
|
|
17
|
-
import type { OAuthController } from "./types";
|
|
16
|
+
import { createApiKeyLogin } from "../api-key-login";
|
|
18
17
|
|
|
19
18
|
const AUTH_URL_INTL = "https://platform.minimax.io/subscribe/token-plan";
|
|
20
19
|
const AUTH_URL_CN = "https://platform.minimaxi.com/subscribe/token-plan";
|
|
@@ -22,61 +21,33 @@ const API_BASE_URL_INTL = "https://api.minimax.io/v1";
|
|
|
22
21
|
const API_BASE_URL_CN = "https://api.minimaxi.com/v1";
|
|
23
22
|
const VALIDATION_MODEL = "MiniMax-M3";
|
|
24
23
|
|
|
24
|
+
function createMiniMaxLogin(authUrl: string, baseUrl: string, provider: string) {
|
|
25
|
+
return createApiKeyLogin({
|
|
26
|
+
providerLabel: "MiniMax Token Plan",
|
|
27
|
+
authUrl,
|
|
28
|
+
instructions: "Subscribe to Token Plan and copy your API key",
|
|
29
|
+
promptMessage: "Paste your MiniMax Token Plan API key",
|
|
30
|
+
placeholder: "sk-...",
|
|
31
|
+
validation: {
|
|
32
|
+
kind: "chat-completions",
|
|
33
|
+
provider,
|
|
34
|
+
baseUrl,
|
|
35
|
+
model: VALIDATION_MODEL,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
25
40
|
/**
|
|
26
41
|
* Login to MiniMax Token Plan (international).
|
|
27
42
|
*
|
|
28
43
|
* Opens browser to subscription page, prompts user to paste their API key.
|
|
29
44
|
* Returns the API key directly (not OAuthCredentials - this isn't OAuth).
|
|
30
45
|
*/
|
|
31
|
-
export
|
|
32
|
-
return loginMiniMaxCodeWithBaseUrl(options, AUTH_URL_INTL, API_BASE_URL_INTL, "MiniMax Token Plan");
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function loginMiniMaxCodeWithBaseUrl(
|
|
36
|
-
options: OAuthController,
|
|
37
|
-
authUrl: string,
|
|
38
|
-
baseUrl: string,
|
|
39
|
-
providerName: string,
|
|
40
|
-
): Promise<string> {
|
|
41
|
-
const fetchImpl = options.fetch ?? fetch;
|
|
42
|
-
if (!options.onPrompt) {
|
|
43
|
-
throw new Error("MiniMax Token Plan login requires onPrompt callback");
|
|
44
|
-
}
|
|
45
|
-
// Open browser to subscription page
|
|
46
|
-
options.onAuth?.({
|
|
47
|
-
url: authUrl,
|
|
48
|
-
instructions: "Subscribe to Token Plan and copy your API key",
|
|
49
|
-
});
|
|
50
|
-
// Prompt user to paste their API key
|
|
51
|
-
const apiKey = await options.onPrompt({
|
|
52
|
-
message: "Paste your MiniMax Token Plan API key",
|
|
53
|
-
placeholder: "sk-...",
|
|
54
|
-
});
|
|
55
|
-
if (options.signal?.aborted) {
|
|
56
|
-
throw new Error("Login cancelled");
|
|
57
|
-
}
|
|
58
|
-
const trimmed = apiKey.trim();
|
|
59
|
-
if (!trimmed) {
|
|
60
|
-
throw new Error("API key is required");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
options.onProgress?.("Validating API key...");
|
|
64
|
-
await validateOpenAICompatibleApiKey({
|
|
65
|
-
provider: providerName,
|
|
66
|
-
apiKey: trimmed,
|
|
67
|
-
baseUrl,
|
|
68
|
-
model: VALIDATION_MODEL,
|
|
69
|
-
signal: options.signal,
|
|
70
|
-
fetch: fetchImpl,
|
|
71
|
-
});
|
|
72
|
-
return trimmed;
|
|
73
|
-
}
|
|
46
|
+
export const loginMiniMaxCode = createMiniMaxLogin(AUTH_URL_INTL, API_BASE_URL_INTL, "MiniMax Token Plan");
|
|
74
47
|
|
|
75
48
|
/**
|
|
76
49
|
* Login to MiniMax Token Plan (China).
|
|
77
50
|
*
|
|
78
51
|
* Same flow as international but uses China endpoint.
|
|
79
52
|
*/
|
|
80
|
-
export
|
|
81
|
-
return loginMiniMaxCodeWithBaseUrl(options, AUTH_URL_CN, API_BASE_URL_CN, "MiniMax Token Plan (China)");
|
|
82
|
-
}
|
|
53
|
+
export const loginMiniMaxCodeCn = createMiniMaxLogin(AUTH_URL_CN, API_BASE_URL_CN, "MiniMax Token Plan (China)");
|
package/src/registry/qianfan.ts
CHANGED
|
@@ -1,46 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
1
|
+
import { createApiKeyLogin } from "./api-key-login";
|
|
2
|
+
import type { OAuthLoginCallbacks } from "./oauth/types";
|
|
3
3
|
import type { ProviderDefinition } from "./types";
|
|
4
4
|
|
|
5
5
|
const AUTH_URL = "https://console.bce.baidu.com/qianfan/ais/console/apiKey";
|
|
6
6
|
const API_BASE_URL = "https://qianfan.baidubce.com/v2";
|
|
7
7
|
const VALIDATION_MODEL = "deepseek-v3.2";
|
|
8
8
|
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
const apiKey = await options.onPrompt({
|
|
20
|
-
message: "Paste your Qianfan API key",
|
|
21
|
-
placeholder: "bce-v3/ALTAK-...",
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
if (options.signal?.aborted) {
|
|
25
|
-
throw new Error("Login cancelled");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const trimmed = apiKey.trim();
|
|
29
|
-
if (!trimmed) {
|
|
30
|
-
throw new Error("API key is required");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
options.onProgress?.("Validating API key...");
|
|
34
|
-
await validateOpenAICompatibleApiKey({
|
|
9
|
+
export const loginQianfan = createApiKeyLogin({
|
|
10
|
+
providerLabel: "Qianfan",
|
|
11
|
+
authUrl: AUTH_URL,
|
|
12
|
+
instructions: "Copy your Qianfan API key from the console",
|
|
13
|
+
promptMessage: "Paste your Qianfan API key",
|
|
14
|
+
placeholder: "bce-v3/ALTAK-...",
|
|
15
|
+
validation: {
|
|
16
|
+
kind: "chat-completions",
|
|
35
17
|
provider: "qianfan",
|
|
36
|
-
apiKey: trimmed,
|
|
37
18
|
baseUrl: API_BASE_URL,
|
|
38
19
|
model: VALIDATION_MODEL,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return trimmed;
|
|
43
|
-
}
|
|
20
|
+
},
|
|
21
|
+
});
|
|
44
22
|
|
|
45
23
|
export const qianfanProvider = {
|
|
46
24
|
id: "qianfan",
|
package/src/registry/registry.ts
CHANGED
|
@@ -44,6 +44,7 @@ import { parallelProvider } from "./parallel";
|
|
|
44
44
|
import { perplexityProvider } from "./perplexity";
|
|
45
45
|
import { qianfanProvider } from "./qianfan";
|
|
46
46
|
import { qwenPortalProvider } from "./qwen-portal";
|
|
47
|
+
import { sakanaProvider } from "./sakana";
|
|
47
48
|
import { syntheticProvider } from "./synthetic";
|
|
48
49
|
import { tavilyProvider } from "./tavily";
|
|
49
50
|
import { togetherProvider } from "./together";
|
|
@@ -90,6 +91,7 @@ const ALL = [
|
|
|
90
91
|
zhipuCodingPlanProvider,
|
|
91
92
|
umansProvider,
|
|
92
93
|
qwenPortalProvider,
|
|
94
|
+
sakanaProvider,
|
|
93
95
|
minimaxCodeProvider,
|
|
94
96
|
minimaxCodeCnProvider,
|
|
95
97
|
xiaomiProvider,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createApiKeyLogin } from "./api-key-login";
|
|
2
|
+
import type { OAuthLoginCallbacks } from "./oauth/types";
|
|
3
|
+
import type { ProviderDefinition } from "./types";
|
|
4
|
+
|
|
5
|
+
export const loginSakana = createApiKeyLogin({
|
|
6
|
+
providerLabel: "Sakana AI",
|
|
7
|
+
authUrl: "https://console.sakana.ai/api-keys",
|
|
8
|
+
instructions: "Copy your API key from the Sakana AI console",
|
|
9
|
+
promptMessage: "Paste your Sakana AI API key",
|
|
10
|
+
placeholder: "sk-...",
|
|
11
|
+
validation: {
|
|
12
|
+
kind: "models-endpoint",
|
|
13
|
+
provider: "Sakana AI",
|
|
14
|
+
modelsUrl: "https://api.sakana.ai/v1/models",
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const sakanaProvider = {
|
|
19
|
+
id: "sakana",
|
|
20
|
+
name: "Sakana AI",
|
|
21
|
+
login: (cb: OAuthLoginCallbacks) => loginSakana(cb),
|
|
22
|
+
} as const satisfies ProviderDefinition;
|
package/src/registry/venice.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
1
|
+
import { createApiKeyLogin } from "./api-key-login";
|
|
2
|
+
import type { OAuthLoginCallbacks } from "./oauth/types";
|
|
3
3
|
import type { ProviderDefinition } from "./types";
|
|
4
4
|
|
|
5
5
|
const AUTH_URL = "https://venice.ai/settings/api";
|
|
@@ -12,41 +12,19 @@ const VALIDATION_MODEL = "qwen3-4b";
|
|
|
12
12
|
* Opens browser to API keys page, prompts user to paste their API key.
|
|
13
13
|
* Returns the API key directly (not OAuthCredentials - this isn't OAuth).
|
|
14
14
|
*/
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
const apiKey = await options.onPrompt({
|
|
26
|
-
message: "Paste your Venice API key",
|
|
27
|
-
placeholder: "vapi_...",
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
if (options.signal?.aborted) {
|
|
31
|
-
throw new Error("Login cancelled");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const trimmed = apiKey.trim();
|
|
35
|
-
if (!trimmed) {
|
|
36
|
-
throw new Error("API key is required");
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
options.onProgress?.("Validating API key...");
|
|
40
|
-
await validateOpenAICompatibleApiKey({
|
|
15
|
+
export const loginVenice = createApiKeyLogin({
|
|
16
|
+
providerLabel: "Venice",
|
|
17
|
+
authUrl: AUTH_URL,
|
|
18
|
+
instructions: "Copy your API key from the Venice dashboard",
|
|
19
|
+
promptMessage: "Paste your Venice API key",
|
|
20
|
+
placeholder: "vapi_...",
|
|
21
|
+
validation: {
|
|
22
|
+
kind: "chat-completions",
|
|
41
23
|
provider: "Venice",
|
|
42
|
-
apiKey: trimmed,
|
|
43
24
|
baseUrl: API_BASE_URL,
|
|
44
25
|
model: VALIDATION_MODEL,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return trimmed;
|
|
49
|
-
}
|
|
26
|
+
},
|
|
27
|
+
});
|
|
50
28
|
|
|
51
29
|
export const veniceProvider = {
|
|
52
30
|
id: "venice",
|
package/src/registry/zai.ts
CHANGED
|
@@ -1,45 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
1
|
+
import { createApiKeyLogin } from "./api-key-login";
|
|
2
|
+
import type { OAuthLoginCallbacks } from "./oauth/types";
|
|
3
3
|
import type { ProviderDefinition } from "./types";
|
|
4
4
|
|
|
5
5
|
const AUTH_URL = "https://z.ai/manage-apikey/apikey-list";
|
|
6
6
|
const API_BASE_URL = "https://api.z.ai/api/coding/paas/v4";
|
|
7
7
|
const VALIDATION_MODEL = "glm-5.2";
|
|
8
8
|
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
const apiKey = await options.onPrompt({
|
|
20
|
-
message: "Paste your Z.AI API key",
|
|
21
|
-
placeholder: "sk-...",
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
if (options.signal?.aborted) {
|
|
25
|
-
throw new Error("Login cancelled");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const trimmed = apiKey.trim();
|
|
29
|
-
if (!trimmed) {
|
|
30
|
-
throw new Error("API key is required");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
options.onProgress?.("Validating API key...");
|
|
34
|
-
await validateOpenAICompatibleApiKey({
|
|
9
|
+
export const loginZai = createApiKeyLogin({
|
|
10
|
+
providerLabel: "Z.AI",
|
|
11
|
+
authUrl: AUTH_URL,
|
|
12
|
+
instructions: "Copy your API key from the dashboard",
|
|
13
|
+
promptMessage: "Paste your Z.AI API key",
|
|
14
|
+
placeholder: "sk-...",
|
|
15
|
+
validation: {
|
|
16
|
+
kind: "chat-completions",
|
|
35
17
|
provider: "Z.AI",
|
|
36
|
-
apiKey: trimmed,
|
|
37
18
|
baseUrl: API_BASE_URL,
|
|
38
19
|
model: VALIDATION_MODEL,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return trimmed;
|
|
42
|
-
}
|
|
20
|
+
},
|
|
21
|
+
});
|
|
43
22
|
|
|
44
23
|
export const zaiProvider = {
|
|
45
24
|
id: "zai",
|
|
@@ -1,45 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
1
|
+
import { createApiKeyLogin } from "./api-key-login";
|
|
2
|
+
import type { OAuthLoginCallbacks } from "./oauth/types";
|
|
3
3
|
import type { ProviderDefinition } from "./types";
|
|
4
4
|
|
|
5
5
|
const AUTH_URL = "https://bigmodel.cn/coding-plan/personal/overview";
|
|
6
6
|
const API_BASE_URL = "https://open.bigmodel.cn/api/coding/paas/v4";
|
|
7
7
|
const VALIDATION_MODEL = "glm-5.1";
|
|
8
8
|
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
const apiKey = await options.onPrompt({
|
|
20
|
-
message: "Paste your Zhipu API key",
|
|
21
|
-
placeholder: "<id>.<secret>",
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
if (options.signal?.aborted) {
|
|
25
|
-
throw new Error("Login cancelled");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const trimmed = apiKey.trim();
|
|
29
|
-
if (!trimmed) {
|
|
30
|
-
throw new Error("API key is required");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
options.onProgress?.("Validating API key...");
|
|
34
|
-
await validateOpenAICompatibleApiKey({
|
|
9
|
+
export const loginZhipuCodingPlan = createApiKeyLogin({
|
|
10
|
+
providerLabel: "Zhipu Coding Plan",
|
|
11
|
+
authUrl: AUTH_URL,
|
|
12
|
+
instructions: "Copy your API key from the Coding Plan dashboard",
|
|
13
|
+
promptMessage: "Paste your Zhipu API key",
|
|
14
|
+
placeholder: "<id>.<secret>",
|
|
15
|
+
validation: {
|
|
16
|
+
kind: "chat-completions",
|
|
35
17
|
provider: "Zhipu",
|
|
36
|
-
apiKey: trimmed,
|
|
37
18
|
baseUrl: API_BASE_URL,
|
|
38
19
|
model: VALIDATION_MODEL,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return trimmed;
|
|
42
|
-
}
|
|
20
|
+
},
|
|
21
|
+
});
|
|
43
22
|
|
|
44
23
|
export const zhipuCodingPlanProvider = {
|
|
45
24
|
id: "zhipu-coding-plan",
|
package/src/stream.ts
CHANGED
|
@@ -64,6 +64,7 @@ import type {
|
|
|
64
64
|
ToolChoice,
|
|
65
65
|
} from "./types";
|
|
66
66
|
import { AssistantMessageEventStream } from "./utils/event-stream";
|
|
67
|
+
import { wrapFetchForProxy } from "./utils/proxy";
|
|
67
68
|
import { withRequestDebugFetch } from "./utils/request-debug";
|
|
68
69
|
import { withGeminiThinkingLoopGuard } from "./utils/thinking-loop";
|
|
69
70
|
|
|
@@ -237,9 +238,12 @@ function streamDispatch<TApi extends Api>(
|
|
|
237
238
|
context: Context,
|
|
238
239
|
options?: OptionsForApi<TApi>,
|
|
239
240
|
): AssistantMessageEventStream {
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
241
|
+
const baseOptions = (options || {}) as StreamOptions;
|
|
242
|
+
const debugOptions = withRequestDebugFetch(baseOptions);
|
|
243
|
+
const requestOptions = {
|
|
244
|
+
...debugOptions,
|
|
245
|
+
fetch: wrapFetchForProxy(debugOptions.fetch ?? (globalThis.fetch as FetchImpl), model.provider),
|
|
246
|
+
} as OptionsForApi<TApi>;
|
|
243
247
|
|
|
244
248
|
// Check custom API registry first (extension-provided APIs like "vertex-claude-api")
|
|
245
249
|
const customApiProvider = getCustomApi(model.api);
|
|
@@ -248,12 +252,12 @@ function streamDispatch<TApi extends Api>(
|
|
|
248
252
|
}
|
|
249
253
|
|
|
250
254
|
if (isGitLabDuoModel(model)) {
|
|
251
|
-
const apiKey =
|
|
255
|
+
const apiKey = requestOptions.apiKey || getEnvApiKey(model.provider);
|
|
252
256
|
if (!apiKey) {
|
|
253
257
|
throw new Error(`No API key for provider: ${model.provider}`);
|
|
254
258
|
}
|
|
255
259
|
return streamGitLabDuo(model, context, {
|
|
256
|
-
...(requestOptions as SimpleStreamOptions
|
|
260
|
+
...(requestOptions as SimpleStreamOptions),
|
|
257
261
|
apiKey,
|
|
258
262
|
});
|
|
259
263
|
}
|
|
@@ -263,14 +267,10 @@ function streamDispatch<TApi extends Api>(
|
|
|
263
267
|
return streamGoogleVertex(model as Model<"google-vertex">, context, requestOptions as GoogleVertexOptions);
|
|
264
268
|
} else if (model.api === "bedrock-converse-stream") {
|
|
265
269
|
// Bedrock doesn't have any API keys instead it sources credentials from standard AWS env variables or from given AWS profile.
|
|
266
|
-
return streamBedrock(
|
|
267
|
-
model as Model<"bedrock-converse-stream">,
|
|
268
|
-
context,
|
|
269
|
-
(requestOptions || {}) as BedrockOptions,
|
|
270
|
-
);
|
|
270
|
+
return streamBedrock(model as Model<"bedrock-converse-stream">, context, requestOptions as BedrockOptions);
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
-
const apiKey = requestOptions
|
|
273
|
+
const apiKey = requestOptions.apiKey || getEnvApiKey(model.provider);
|
|
274
274
|
if (!apiKey) {
|
|
275
275
|
throw new Error(`No API key for provider: ${model.provider}`);
|
|
276
276
|
}
|
|
@@ -278,7 +278,7 @@ function streamDispatch<TApi extends Api>(
|
|
|
278
278
|
? {
|
|
279
279
|
...requestOptions,
|
|
280
280
|
apiKey: "vertex-adc",
|
|
281
|
-
fetch: createVertexAuthenticatedFetch(requestOptions
|
|
281
|
+
fetch: createVertexAuthenticatedFetch(requestOptions),
|
|
282
282
|
}
|
|
283
283
|
: { ...requestOptions, apiKey };
|
|
284
284
|
|
|
@@ -410,7 +410,12 @@ export function streamSimple<TApi extends Api>(
|
|
|
410
410
|
context: Context,
|
|
411
411
|
options?: SimpleStreamOptions,
|
|
412
412
|
): AssistantMessageEventStream {
|
|
413
|
-
const
|
|
413
|
+
const baseOptions = (options || {}) as SimpleStreamOptions;
|
|
414
|
+
const debugOptions = withRequestDebugFetch(baseOptions);
|
|
415
|
+
const requestOptions = {
|
|
416
|
+
...debugOptions,
|
|
417
|
+
fetch: wrapFetchForProxy(debugOptions.fetch ?? (globalThis.fetch as FetchImpl), model.provider),
|
|
418
|
+
} as SimpleStreamOptions;
|
|
414
419
|
const apiKeyResolver = isApiKeyResolver(requestOptions?.apiKey) ? requestOptions.apiKey : undefined;
|
|
415
420
|
if (apiKeyResolver) {
|
|
416
421
|
const outer = new AssistantMessageEventStream();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A UUID-shaped string: five hex groups in the canonical 8-4-4-4-12 layout.
|
|
3
|
+
*
|
|
4
|
+
* NOT a spec-compliant RFC 4122 UUID — the version/variant nibbles are left as
|
|
5
|
+
* raw hash output — but the shape passes everywhere a UUID string is expected.
|
|
6
|
+
*/
|
|
7
|
+
export type DeterministicUuid = `${string}-${string}-${string}-${string}-${string}`;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Format the leading 128 bits of `seed`'s SHA-256 digest as a v4-shape UUID
|
|
11
|
+
* (8-4-4-4-12 hex groups).
|
|
12
|
+
*
|
|
13
|
+
* Deterministic: identical seeds always map to the same id, so callers get
|
|
14
|
+
* stable ids across requests / conversation turns (reusing message-blob ids,
|
|
15
|
+
* keying prompt caches) without persisting a seed→id mapping.
|
|
16
|
+
*/
|
|
17
|
+
export function deterministicUuid(seed: string): DeterministicUuid {
|
|
18
|
+
const hex = new Bun.CryptoHasher("sha256").update(seed).digest("hex");
|
|
19
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
20
|
+
}
|