jeopi-ai 16.2.24 → 16.2.26
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 +13 -0
- package/dist/types/dialect/demotion.d.ts +3 -3
- package/dist/types/registry/api-key-login.d.ts +4 -0
- package/dist/types/registry/api-key-validation.d.ts +4 -0
- package/dist/types/registry/registry.d.ts +4 -0
- package/dist/types/registry/tencent.d.ts +7 -0
- package/package.json +4 -4
- package/src/dialect/demotion.ts +5 -8
- package/src/registry/api-key-login.ts +5 -0
- package/src/registry/api-key-validation.ts +5 -1
- package/src/registry/registry.ts +2 -0
- package/src/registry/tencent.ts +33 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [16.2.26] - 2026-07-05
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- `renderDemotedThinking`'s classifier-safe markdown-italic prose form (avoids Anthropic's `reasoning_extraction` refusal when replaying a prior turn's unsigned reasoning as plaintext) was keyed off a hand-rolled `claude-fable`-only regex, missing `claude-mythos-5` — a real first-party Anthropic model the rest of the catalog already treats as classifier-equivalent to Fable (`isAnthropicFableOrMythosModel`). Unsigned Mythos thinking replayed cross-model/branch-handoff was demoted to a raw `<thinking>...</thinking>` text block instead, which can trip the same reasoning-extraction classifier Fable's carve-out exists to avoid. Now uses the canonical `isAnthropicFableOrMythosModel` classifier for both.
|
|
10
|
+
|
|
11
|
+
## [16.2.25] - 2026-07-05
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Added the `tencent` provider registry def (`registry/tencent.ts`): API-key login validated against Tencent Cloud MaaS's Anthropic Messages endpoint (`tokenhub-intl.tencentcloudmaas.com/v1/messages`), registered in the `ALL` provider list. Env fallback: `TENCENT_API_KEY`. Verified live: full inference round-trip (GLM-5.2, Hunyuan MT2 Plus) succeeds end-to-end through `streamAnthropic`; DeepSeek/Kimi ids on the same test key correctly surfaced a clean `402` (quota exhausted) rather than an error.
|
|
16
|
+
- `createApiKeyLogin`'s `anthropic-messages` validation now accepts an optional `acceptableErrorStatuses` list: HTTP statuses that still prove the key is valid (e.g. a billing/quota gate only a real, authenticated key would reach) and should pass validation instead of being treated as an invalid key. Discovered live while validating the `tencent` login: a real, correctly-authenticated key with no active billing got HTTP 402, which the login flow was previously rejecting as "invalid key" — `tencent`'s validation now sets `acceptableErrorStatuses: [402]`; other providers are unaffected (opt-in, defaults to none).
|
|
17
|
+
|
|
5
18
|
## [16.2.24] - 2026-07-03
|
|
6
19
|
|
|
7
20
|
### Added
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* replayed unsigned `thought` part is schema-accepted but silently discarded —
|
|
6
6
|
* neither recalled nor influencing generation).
|
|
7
7
|
*
|
|
8
|
-
* Fable
|
|
8
|
+
* Fable/Mythos are the exception: replaying prior reasoning inside `<thinking>` /
|
|
9
9
|
* `antml:thinking`-style assistant text is treated as a reasoning-extraction
|
|
10
|
-
* attempt and can train the next turn to leak thoughts, so Fable
|
|
11
|
-
* reasoning as markdown-italic assistant prose instead. Harmony and Gemma are
|
|
10
|
+
* attempt and can train the next turn to leak thoughts, so Fable/Mythos receive
|
|
11
|
+
* the reasoning as markdown-italic assistant prose instead. Harmony and Gemma are
|
|
12
12
|
* also exceptions: their `renderThinking` emits chat-template control tokens
|
|
13
13
|
* (`<|channel|>analysis`, `<|channel>thought`) that must not appear inside a
|
|
14
14
|
* structured native message, so they fall back to a plain `<think>` block. Every
|
|
@@ -17,6 +17,10 @@ type AnthropicMessagesValidation = {
|
|
|
17
17
|
provider: string;
|
|
18
18
|
baseUrl: string;
|
|
19
19
|
model: string;
|
|
20
|
+
/** HTTP error statuses that still prove the key is valid (e.g. a billing/quota
|
|
21
|
+
* gate that only a real, recognized key would reach) and should be treated as
|
|
22
|
+
* successful validation rather than an invalid-key failure. */
|
|
23
|
+
acceptableErrorStatuses?: readonly number[];
|
|
20
24
|
};
|
|
21
25
|
type ModelsEndpointValidation = {
|
|
22
26
|
kind: "models-endpoint";
|
|
@@ -14,6 +14,10 @@ type AnthropicCompatibleValidationOptions = {
|
|
|
14
14
|
model: string;
|
|
15
15
|
signal?: AbortSignal;
|
|
16
16
|
fetch?: FetchImpl;
|
|
17
|
+
/** HTTP error statuses that still prove the key is valid (e.g. a billing/quota
|
|
18
|
+
* gate that only a real, recognized key would reach) and should be treated as
|
|
19
|
+
* successful validation rather than an invalid-key failure. */
|
|
20
|
+
acceptableErrorStatuses?: readonly number[];
|
|
17
21
|
};
|
|
18
22
|
type ModelListValidationOptions = {
|
|
19
23
|
provider: string;
|
|
@@ -232,6 +232,10 @@ declare const ALL: ({
|
|
|
232
232
|
readonly name: "Tavily";
|
|
233
233
|
readonly envKeys: "TAVILY_API_KEY";
|
|
234
234
|
readonly login: (cb: import("./oauth").OAuthLoginCallbacks) => Promise<string>;
|
|
235
|
+
} | {
|
|
236
|
+
readonly id: "tencent";
|
|
237
|
+
readonly name: "Tencent Cloud MaaS";
|
|
238
|
+
readonly login: (cb: import("./oauth").OAuthLoginCallbacks) => Promise<string>;
|
|
235
239
|
} | {
|
|
236
240
|
readonly id: "together";
|
|
237
241
|
readonly name: "Together";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { OAuthLoginCallbacks } from "./oauth/types";
|
|
2
|
+
export declare const loginTencent: (options: import("./oauth").OAuthController) => Promise<string>;
|
|
3
|
+
export declare const tencentProvider: {
|
|
4
|
+
readonly id: "tencent";
|
|
5
|
+
readonly name: "Tencent Cloud MaaS";
|
|
6
|
+
readonly login: (cb: OAuthLoginCallbacks) => Promise<string>;
|
|
7
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "jeopi-ai",
|
|
4
|
-
"version": "16.2.
|
|
4
|
+
"version": "16.2.26",
|
|
5
5
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
6
6
|
"homepage": "https://github.com/akillness/jeopi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@bufbuild/protobuf": "^2.12.0",
|
|
41
|
-
"jeopi-catalog": "16.2.
|
|
42
|
-
"jeopi-utils": "16.2.
|
|
43
|
-
"jeopi-wire": "16.2.
|
|
41
|
+
"jeopi-catalog": "16.2.26",
|
|
42
|
+
"jeopi-utils": "16.2.26",
|
|
43
|
+
"jeopi-wire": "16.2.26",
|
|
44
44
|
"arktype": "^2.2.0",
|
|
45
45
|
"zod": "^4"
|
|
46
46
|
},
|
package/src/dialect/demotion.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isAnthropicFableOrMythosModel, preferredDialect } from "jeopi-catalog/identity";
|
|
2
2
|
import { getDialectDefinition } from "./factory";
|
|
3
3
|
|
|
4
|
-
const CLAUDE_FABLE_ID = /(?:^|[./])claude[-.]fable(?:[-.]|$)/i;
|
|
5
|
-
|
|
6
4
|
/**
|
|
7
5
|
* Wrap a prior-turn reasoning string for demotion into native conversation
|
|
8
6
|
* history — the cross-provider / cross-model case where the target cannot replay
|
|
@@ -10,10 +8,10 @@ const CLAUDE_FABLE_ID = /(?:^|[./])claude[-.]fable(?:[-.]|$)/i;
|
|
|
10
8
|
* replayed unsigned `thought` part is schema-accepted but silently discarded —
|
|
11
9
|
* neither recalled nor influencing generation).
|
|
12
10
|
*
|
|
13
|
-
* Fable
|
|
11
|
+
* Fable/Mythos are the exception: replaying prior reasoning inside `<thinking>` /
|
|
14
12
|
* `antml:thinking`-style assistant text is treated as a reasoning-extraction
|
|
15
|
-
* attempt and can train the next turn to leak thoughts, so Fable
|
|
16
|
-
* reasoning as markdown-italic assistant prose instead. Harmony and Gemma are
|
|
13
|
+
* attempt and can train the next turn to leak thoughts, so Fable/Mythos receive
|
|
14
|
+
* the reasoning as markdown-italic assistant prose instead. Harmony and Gemma are
|
|
17
15
|
* also exceptions: their `renderThinking` emits chat-template control tokens
|
|
18
16
|
* (`<|channel|>analysis`, `<|channel>thought`) that must not appear inside a
|
|
19
17
|
* structured native message, so they fall back to a plain `<think>` block. Every
|
|
@@ -28,9 +26,8 @@ const CLAUDE_FABLE_ID = /(?:^|[./])claude[-.]fable(?:[-.]|$)/i;
|
|
|
28
26
|
export function renderDemotedThinking(modelId: string, text: string): string {
|
|
29
27
|
if (!text) return "";
|
|
30
28
|
text = text.toWellFormed();
|
|
31
|
-
const canonicalId = bareModelId(modelId);
|
|
32
29
|
const dialect = preferredDialect(modelId);
|
|
33
|
-
if (
|
|
30
|
+
if (isAnthropicFableOrMythosModel(modelId)) return `_Hmm. ${text}_\n`;
|
|
34
31
|
if (dialect === "harmony" || dialect === "gemma") return `<think>\n${text}\n</think>\n`;
|
|
35
32
|
return `${getDialectDefinition(dialect).renderThinking(text)}\n`;
|
|
36
33
|
}
|
|
@@ -26,6 +26,10 @@ type AnthropicMessagesValidation = {
|
|
|
26
26
|
provider: string;
|
|
27
27
|
baseUrl: string;
|
|
28
28
|
model: string;
|
|
29
|
+
/** HTTP error statuses that still prove the key is valid (e.g. a billing/quota
|
|
30
|
+
* gate that only a real, recognized key would reach) and should be treated as
|
|
31
|
+
* successful validation rather than an invalid-key failure. */
|
|
32
|
+
acceptableErrorStatuses?: readonly number[];
|
|
29
33
|
};
|
|
30
34
|
|
|
31
35
|
type ModelsEndpointValidation = {
|
|
@@ -92,6 +96,7 @@ export function createApiKeyLogin(config: ApiKeyLoginConfig): (options: OAuthCon
|
|
|
92
96
|
apiKey: trimmed,
|
|
93
97
|
baseUrl: config.validation.baseUrl,
|
|
94
98
|
model: config.validation.model,
|
|
99
|
+
acceptableErrorStatuses: config.validation.acceptableErrorStatuses,
|
|
95
100
|
signal: options.signal,
|
|
96
101
|
fetch: options.fetch,
|
|
97
102
|
});
|
|
@@ -16,6 +16,10 @@ type AnthropicCompatibleValidationOptions = {
|
|
|
16
16
|
model: string;
|
|
17
17
|
signal?: AbortSignal;
|
|
18
18
|
fetch?: FetchImpl;
|
|
19
|
+
/** HTTP error statuses that still prove the key is valid (e.g. a billing/quota
|
|
20
|
+
* gate that only a real, recognized key would reach) and should be treated as
|
|
21
|
+
* successful validation rather than an invalid-key failure. */
|
|
22
|
+
acceptableErrorStatuses?: readonly number[];
|
|
19
23
|
};
|
|
20
24
|
|
|
21
25
|
type ModelListValidationOptions = {
|
|
@@ -106,7 +110,7 @@ export async function validateAnthropicCompatibleApiKey(options: AnthropicCompat
|
|
|
106
110
|
signal,
|
|
107
111
|
});
|
|
108
112
|
|
|
109
|
-
if (response.ok) {
|
|
113
|
+
if (response.ok || options.acceptableErrorStatuses?.includes(response.status)) {
|
|
110
114
|
return;
|
|
111
115
|
}
|
|
112
116
|
|
package/src/registry/registry.ts
CHANGED
|
@@ -49,6 +49,7 @@ import { qwenPortalProvider } from "./qwen-portal";
|
|
|
49
49
|
import { sakanaProvider } from "./sakana";
|
|
50
50
|
import { syntheticProvider } from "./synthetic";
|
|
51
51
|
import { tavilyProvider } from "./tavily";
|
|
52
|
+
import { tencentProvider } from "./tencent";
|
|
52
53
|
import { togetherProvider } from "./together";
|
|
53
54
|
import type { ProviderDefinition } from "./types";
|
|
54
55
|
import { umansProvider } from "./umans";
|
|
@@ -139,6 +140,7 @@ const ALL = [
|
|
|
139
140
|
mistralProvider,
|
|
140
141
|
minimaxProvider,
|
|
141
142
|
amazonBedrockProvider,
|
|
143
|
+
tencentProvider,
|
|
142
144
|
];
|
|
143
145
|
|
|
144
146
|
export type RegistryDef = (typeof ALL)[number];
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createApiKeyLogin } from "./api-key-login";
|
|
2
|
+
import type { OAuthLoginCallbacks } from "./oauth/types";
|
|
3
|
+
import type { ProviderDefinition } from "./types";
|
|
4
|
+
|
|
5
|
+
const AUTH_URL = "https://cloud.tencent.com/product/lke";
|
|
6
|
+
const API_BASE_URL = "https://tokenhub-intl.tencentcloudmaas.com";
|
|
7
|
+
const VALIDATION_MODEL = "deepseek-v4-pro";
|
|
8
|
+
|
|
9
|
+
export const loginTencent = createApiKeyLogin({
|
|
10
|
+
providerLabel: "Tencent Cloud MaaS",
|
|
11
|
+
authUrl: AUTH_URL,
|
|
12
|
+
instructions: "Create or copy your Tencent Cloud MaaS API key from the console.",
|
|
13
|
+
promptMessage: "Paste your Tencent Cloud MaaS API key",
|
|
14
|
+
placeholder: "sk-...",
|
|
15
|
+
validation: {
|
|
16
|
+
kind: "anthropic-messages",
|
|
17
|
+
provider: "Tencent Cloud MaaS",
|
|
18
|
+
baseUrl: API_BASE_URL,
|
|
19
|
+
model: VALIDATION_MODEL,
|
|
20
|
+
// Verified live (2026-07): a recognized-but-unbilled key gets HTTP 402
|
|
21
|
+
// ("free trial quota exhausted ... postpaid billing not enabled"), not a
|
|
22
|
+
// 401/403 — the gateway only returns 402 after successfully authenticating
|
|
23
|
+
// the key. Treat it as a valid credential so a real key isn't rejected as
|
|
24
|
+
// "invalid" just because the account has no active billing yet.
|
|
25
|
+
acceptableErrorStatuses: [402],
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const tencentProvider = {
|
|
30
|
+
id: "tencent",
|
|
31
|
+
name: "Tencent Cloud MaaS",
|
|
32
|
+
login: (cb: OAuthLoginCallbacks) => loginTencent(cb),
|
|
33
|
+
} as const satisfies ProviderDefinition;
|