feat(tencent): add bundled Tencent Cloud provider plugin (Tokenhub + Token Plan) (#68460)

* feat(tencent): add bundled Tencent Cloud provider plugin (Tokenhub + Token Plan)

* fix(tencent): use provider-specific default model aliases

Both Tencent providers previously defaulted to the same alias "HY3 Preview",
which collides in buildModelAliasIndex (single alias map, keyed by normalized
alias). When both providers are onboarded, alias-based selection routed to
whichever provider was configured last.

Disambiguate the fallback aliases so resolution is deterministic regardless
of onboarding order:
  - tencent-tokenhub   -> "HY3 Preview (TokenHub)"
  - tencent-token-plan -> "HY3 Preview (Token Plan)"

* docs(tencent): rename model to "Hy3 preview" and drop "HY3" family name

Align with the external-facing product name:
  - model display name: "HY3 Preview" -> "Hy3 preview"
  - family/umbrella references in docs and auth hints: "HY3" -> "Hy3 preview"
  - internal cost constant: HY3_COST -> HY_COST

Model call id (hy3-preview) is unchanged.

* docs(tencent): use "Hy" as the family name in generic references

Keep specific model references as "Hy3 preview" (model catalog names,
onboarding aliases, Available-models docs entries), but switch
family/umbrella references to the plain "Hy" family name so future Hy
versions fit without doc churn:

  - auth hints: "Hy via Tencent TokenHub Gateway" / "Hy via Token Plan"
  - docs intro + Use-case table: "Tencent Hy models" / "call Hy via ..."
  - models.ts pricing comment: "Hy pricing"

* feat(tencent): add tiered pricing for Hy3 preview model

---------

Co-authored-by: albertxyu <albertxyu@tencent.com>
This commit is contained in:
JuniperSling
2026-04-22 12:59:22 +08:00
committed by GitHub
parent c07b388f77
commit d8b9be468a
13 changed files with 494 additions and 0 deletions

View File

@@ -54,6 +54,8 @@ OPENCLAW_GATEWAY_TOKEN=
# Optional additional providers
# ZAI_API_KEY=...
# AI_GATEWAY_API_KEY=...
# TOKENHUB_API_KEY=...
# LKEAP_API_KEY=...
# MINIMAX_API_KEY=...
# SYNTHETIC_API_KEY=...

4
.github/labeler.yml vendored
View File

@@ -269,6 +269,10 @@
- changed-files:
- any-glob-to-any-file:
- "extensions/deepseek/**"
"extensions: tencent":
- changed-files:
- any-glob-to-any-file:
- "extensions/tencent/**"
"extensions: stepfun":
- changed-files:
- any-glob-to-any-file:

View File

@@ -1292,6 +1292,7 @@
"providers/sglang",
"providers/stepfun",
"providers/synthetic",
"providers/tencent",
"providers/together",
"providers/venice",
"providers/vercel-ai-gateway",

90
docs/providers/tencent.md Normal file
View File

@@ -0,0 +1,90 @@
---
title: "Tencent Cloud (TokenHub + Token Plan)"
summary: "Tencent Cloud TokenHub and Token Plan setup (separate keys)"
read_when:
- You want to use Tencent Hy models with OpenClaw
- You need the TokenHub API key or Token Plan (LKEAP) setup
---
# Tencent Cloud (TokenHub + Token Plan)
The Tencent Cloud provider gives access to Tencent Hy models via two endpoints
with separate API keys:
- **TokenHub** (`tencent-tokenhub`) — call Hy via Tencent TokenHub Gateway
- **Token Plan** (`tencent-token-plan`) — call Hy via the LKEAP
Token Plan endpoint
Both providers use OpenAI-compatible APIs.
## Quick start
TokenHub:
```bash
openclaw onboard --auth-choice tokenhub-api-key
```
Token Plan:
```bash
openclaw onboard --auth-choice tencent-token-plan-api-key
```
## Non-interactive example
```bash
# TokenHub
openclaw onboard --non-interactive \
--mode local \
--auth-choice tokenhub-api-key \
--tokenhub-api-key "$TOKENHUB_API_KEY" \
--skip-health \
--accept-risk
# Token Plan
openclaw onboard --non-interactive \
--mode local \
--auth-choice tencent-token-plan-api-key \
--tencent-token-plan-api-key "$LKEAP_API_KEY" \
--skip-health \
--accept-risk
```
## Providers and endpoints
| Provider | Endpoint | Use case |
| -------------------- | ------------------------------------- | ----------------------- |
| `tencent-tokenhub` | `tokenhub.tencentmaas.com/v1` | Hy via Tencent TokenHub |
| `tencent-token-plan` | `api.lkeap.cloud.tencent.com/plan/v3` | Hy via LKEAP Token Plan |
Each provider uses its own API key. Setup registers only the selected provider.
## Available models
### tencent-tokenhub
- **hy3-preview** — Hy3 preview (256K context, reasoning, default)
### tencent-token-plan
- **hy3-preview** — Hy3 preview (256K context, reasoning, default)
## Notes
- TokenHub model refs use `tencent-tokenhub/<modelId>`. Token Plan model refs
use `tencent-token-plan/<modelId>`.
- Override pricing and context metadata in `models.providers` if needed.
## Environment note
If the Gateway runs as a daemon (launchd/systemd), make sure `TOKENHUB_API_KEY`
or `LKEAP_API_KEY` is available to that process (for example, in
`~/.openclaw/.env` or via `env.shellEnv`).
## Related documentation
- [OpenClaw Configuration](/configuration)
- [Model Providers](/concepts/model-providers)
- [Tencent TokenHub](https://cloud.tencent.com/document/product/1823/130050)
- [Tencent Token Plan API](https://cloud.tencent.com/document/product/1823/130060)

11
extensions/tencent/api.ts Normal file
View File

@@ -0,0 +1,11 @@
export {
buildTokenHubModelDefinition,
buildTokenPlanModelDefinition,
TOKENHUB_BASE_URL,
TOKENHUB_MODEL_CATALOG,
TOKENHUB_PROVIDER_ID,
TOKEN_PLAN_BASE_URL,
TOKEN_PLAN_MODEL_CATALOG,
TOKEN_PLAN_PROVIDER_ID,
} from "./models.js";
export { buildTokenHubProvider, buildTokenPlanProvider } from "./provider-catalog.js";

116
extensions/tencent/index.ts Normal file
View File

@@ -0,0 +1,116 @@
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import { buildSingleProviderApiKeyCatalog } from "openclaw/plugin-sdk/provider-catalog-shared";
import {
TOKENHUB_MODEL_CATALOG,
TOKENHUB_PROVIDER_ID,
TOKEN_PLAN_MODEL_CATALOG,
TOKEN_PLAN_PROVIDER_ID,
} from "./models.js";
import {
applyTokenHubConfig,
TOKENHUB_DEFAULT_MODEL_REF,
applyTokenPlanConfig,
TOKEN_PLAN_DEFAULT_MODEL_REF,
} from "./onboard.js";
import { buildTokenHubProvider, buildTokenPlanProvider } from "./provider-catalog.js";
function buildStaticCatalogEntries(providerId: string, catalog: typeof TOKENHUB_MODEL_CATALOG) {
return catalog.map((entry) => ({
provider: providerId,
id: entry.id,
name: entry.name,
reasoning: entry.reasoning,
input: [...entry.input],
contextWindow: entry.contextWindow,
}));
}
export default definePluginEntry({
id: "tencent",
name: "Tencent Cloud Provider",
description: "Bundled Tencent Cloud provider plugins (TokenHub + Token Plan)",
register(api) {
// ---------- TokenHub provider ----------
api.registerProvider({
id: TOKENHUB_PROVIDER_ID,
label: "Tencent TokenHub",
docsPath: "/providers/tencent",
envVars: ["TOKENHUB_API_KEY"],
auth: [
createProviderApiKeyAuthMethod({
providerId: TOKENHUB_PROVIDER_ID,
methodId: "api-key",
label: "Tencent TokenHub",
hint: "Hy via Tencent TokenHub Gateway",
optionKey: "tokenhubApiKey",
flagName: "--tokenhub-api-key",
envVar: "TOKENHUB_API_KEY",
promptMessage: "Enter Tencent TokenHub API key",
defaultModel: TOKENHUB_DEFAULT_MODEL_REF,
expectedProviders: [TOKENHUB_PROVIDER_ID],
applyConfig: (cfg) => applyTokenHubConfig(cfg),
wizard: {
choiceId: "tokenhub-api-key",
choiceLabel: "Tencent TokenHub",
groupId: "tencent",
groupLabel: "Tencent Cloud",
groupHint: "TokenHub + Token Plan",
},
}),
],
catalog: {
order: "simple",
run: (ctx) =>
buildSingleProviderApiKeyCatalog({
ctx,
providerId: TOKENHUB_PROVIDER_ID,
buildProvider: buildTokenHubProvider,
}),
},
augmentModelCatalog: () =>
buildStaticCatalogEntries(TOKENHUB_PROVIDER_ID, TOKENHUB_MODEL_CATALOG),
});
// ---------- Token Plan provider ----------
api.registerProvider({
id: TOKEN_PLAN_PROVIDER_ID,
label: "Tencent Token Plan",
docsPath: "/providers/tencent",
envVars: ["LKEAP_API_KEY"],
auth: [
createProviderApiKeyAuthMethod({
providerId: TOKEN_PLAN_PROVIDER_ID,
methodId: "api-key",
label: "Tencent Token Plan",
hint: "Hy via Token Plan",
optionKey: "tencentTokenPlanApiKey",
flagName: "--tencent-token-plan-api-key",
envVar: "LKEAP_API_KEY",
promptMessage: "Enter Tencent Token Plan API key",
defaultModel: TOKEN_PLAN_DEFAULT_MODEL_REF,
expectedProviders: [TOKEN_PLAN_PROVIDER_ID],
applyConfig: (cfg) => applyTokenPlanConfig(cfg),
wizard: {
choiceId: "tencent-token-plan-api-key",
choiceLabel: "Tencent Token Plan",
groupId: "tencent",
groupLabel: "Tencent Cloud",
groupHint: "TokenHub + Token Plan",
},
}),
],
catalog: {
order: "simple",
run: (ctx) =>
buildSingleProviderApiKeyCatalog({
ctx,
providerId: TOKEN_PLAN_PROVIDER_ID,
buildProvider: buildTokenPlanProvider,
}),
},
augmentModelCatalog: () =>
buildStaticCatalogEntries(TOKEN_PLAN_PROVIDER_ID, TOKEN_PLAN_MODEL_CATALOG),
});
},
});

View File

@@ -0,0 +1,93 @@
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
// ---------- TokenHub provider ----------
export const TOKENHUB_BASE_URL = "https://tokenhub.tencentmaas.com/v1";
export const TOKENHUB_PROVIDER_ID = "tencent-tokenhub";
// Hy3 preview pricing ($ per 1M tokens), tiered by input context length.
// Flat rates mirror the first tier; tieredPricing drives actual cost calculation.
const HY3_PREVIEW_COST = {
input: 0.176,
output: 0.587,
cacheRead: 0.059,
cacheWrite: 0,
tieredPricing: [
{
input: 0.176,
output: 0.587,
cacheRead: 0.059,
cacheWrite: 0,
range: [0, 16_000] as [number, number],
},
{
input: 0.235,
output: 0.939,
cacheRead: 0.088,
cacheWrite: 0,
range: [16_000, 32_000] as [number, number],
},
{
input: 0.293,
output: 1.173,
cacheRead: 0.117,
cacheWrite: 0,
range: [32_000] as [number],
},
],
};
export const TOKENHUB_MODEL_CATALOG: ModelDefinitionConfig[] = [
{
id: "hy3-preview",
name: "Hy3 preview (TokenHub)",
reasoning: true,
input: ["text"],
contextWindow: 256_000,
maxTokens: 64_000,
cost: HY3_PREVIEW_COST,
compat: {
supportsUsageInStreaming: true,
supportsReasoningEffort: true,
},
},
];
export function buildTokenHubModelDefinition(
model: (typeof TOKENHUB_MODEL_CATALOG)[number],
): ModelDefinitionConfig {
return {
...model,
api: "openai-completions",
};
}
// ---------- Token Plan provider ----------
export const TOKEN_PLAN_BASE_URL = "https://api.lkeap.cloud.tencent.com/plan/v3";
export const TOKEN_PLAN_PROVIDER_ID = "tencent-token-plan";
export const TOKEN_PLAN_MODEL_CATALOG: ModelDefinitionConfig[] = [
{
id: "hy3-preview",
name: "Hy3 preview (Token Plan)",
reasoning: true,
input: ["text"],
contextWindow: 256_000,
maxTokens: 64_000,
cost: HY3_PREVIEW_COST,
compat: {
supportsUsageInStreaming: true,
supportsReasoningEffort: true,
},
},
];
export function buildTokenPlanModelDefinition(
model: (typeof TOKEN_PLAN_MODEL_CATALOG)[number],
): ModelDefinitionConfig {
return {
...model,
api: "openai-completions",
};
}

View File

@@ -0,0 +1,73 @@
import {
applyAgentDefaultModelPrimary,
applyProviderConfigWithModelCatalog,
type OpenClawConfig,
} from "openclaw/plugin-sdk/provider-onboard";
import {
buildTokenHubModelDefinition,
buildTokenPlanModelDefinition,
TOKENHUB_BASE_URL,
TOKENHUB_MODEL_CATALOG,
TOKENHUB_PROVIDER_ID,
TOKEN_PLAN_BASE_URL,
TOKEN_PLAN_MODEL_CATALOG,
TOKEN_PLAN_PROVIDER_ID,
} from "./api.js";
// ---------- TokenHub ----------
export const TOKENHUB_DEFAULT_MODEL_REF = `${TOKENHUB_PROVIDER_ID}/hy3-preview`;
function applyTokenHubProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
const models = { ...cfg.agents?.defaults?.models };
models[TOKENHUB_DEFAULT_MODEL_REF] = {
...models[TOKENHUB_DEFAULT_MODEL_REF],
// Provider-specific alias to keep alias resolution deterministic when
// both Tencent providers are enabled (see buildModelAliasIndex).
alias: models[TOKENHUB_DEFAULT_MODEL_REF]?.alias ?? "Hy3 preview (TokenHub)",
};
return applyProviderConfigWithModelCatalog(cfg, {
agentModels: models,
providerId: TOKENHUB_PROVIDER_ID,
api: "openai-completions",
baseUrl: TOKENHUB_BASE_URL,
catalogModels: TOKENHUB_MODEL_CATALOG.map(buildTokenHubModelDefinition),
});
}
export function applyTokenHubConfig(cfg: OpenClawConfig): OpenClawConfig {
return applyAgentDefaultModelPrimary(
applyTokenHubProviderConfig(cfg),
TOKENHUB_DEFAULT_MODEL_REF,
);
}
// ---------- Token Plan ----------
export const TOKEN_PLAN_DEFAULT_MODEL_REF = `${TOKEN_PLAN_PROVIDER_ID}/hy3-preview`;
function applyTokenPlanProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
const models = { ...cfg.agents?.defaults?.models };
models[TOKEN_PLAN_DEFAULT_MODEL_REF] = {
...models[TOKEN_PLAN_DEFAULT_MODEL_REF],
// Provider-specific alias to keep alias resolution deterministic when
// both Tencent providers are enabled (see buildModelAliasIndex).
alias: models[TOKEN_PLAN_DEFAULT_MODEL_REF]?.alias ?? "Hy3 preview (Token Plan)",
};
return applyProviderConfigWithModelCatalog(cfg, {
agentModels: models,
providerId: TOKEN_PLAN_PROVIDER_ID,
api: "openai-completions",
baseUrl: TOKEN_PLAN_BASE_URL,
catalogModels: TOKEN_PLAN_MODEL_CATALOG.map(buildTokenPlanModelDefinition),
});
}
export function applyTokenPlanConfig(cfg: OpenClawConfig): OpenClawConfig {
return applyAgentDefaultModelPrimary(
applyTokenPlanProviderConfig(cfg),
TOKEN_PLAN_DEFAULT_MODEL_REF,
);
}

View File

@@ -0,0 +1,42 @@
{
"id": "tencent",
"enabledByDefault": true,
"providers": ["tencent-tokenhub", "tencent-token-plan"],
"providerAuthEnvVars": {
"tencent-tokenhub": ["TOKENHUB_API_KEY"],
"tencent-token-plan": ["LKEAP_API_KEY"]
},
"providerAuthChoices": [
{
"provider": "tencent-tokenhub",
"method": "api-key",
"choiceId": "tokenhub-api-key",
"choiceLabel": "Tencent TokenHub",
"groupId": "tencent",
"groupLabel": "Tencent Cloud",
"groupHint": "TokenHub + Token Plan",
"optionKey": "tokenhubApiKey",
"cliFlag": "--tokenhub-api-key",
"cliOption": "--tokenhub-api-key <key>",
"cliDescription": "Tencent TokenHub API key"
},
{
"provider": "tencent-token-plan",
"method": "api-key",
"choiceId": "tencent-token-plan-api-key",
"choiceLabel": "Tencent Token Plan",
"groupId": "tencent",
"groupLabel": "Tencent Cloud",
"groupHint": "TokenHub + Token Plan",
"optionKey": "tencentTokenPlanApiKey",
"cliFlag": "--tencent-token-plan-api-key",
"cliOption": "--tencent-token-plan-api-key <key>",
"cliDescription": "Tencent Token Plan API key"
}
],
"configSchema": {
"type": "object",
"additionalProperties": false,
"properties": {}
}
}

View File

@@ -0,0 +1,15 @@
{
"name": "@openclaw/tencent-provider",
"version": "2026.4.10",
"private": true,
"description": "OpenClaw Tencent Cloud provider plugin (TokenHub + Token Plan)",
"type": "module",
"devDependencies": {
"@openclaw/plugin-sdk": "workspace:*"
},
"openclaw": {
"extensions": [
"./index.ts"
]
}
}

View File

@@ -0,0 +1,25 @@
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-shared";
import {
buildTokenHubModelDefinition,
buildTokenPlanModelDefinition,
TOKENHUB_BASE_URL,
TOKENHUB_MODEL_CATALOG,
TOKEN_PLAN_BASE_URL,
TOKEN_PLAN_MODEL_CATALOG,
} from "./models.js";
export function buildTokenHubProvider(): ModelProviderConfig {
return {
baseUrl: TOKENHUB_BASE_URL,
api: "openai-completions",
models: TOKENHUB_MODEL_CATALOG.map(buildTokenHubModelDefinition),
};
}
export function buildTokenPlanProvider(): ModelProviderConfig {
return {
baseUrl: TOKEN_PLAN_BASE_URL,
api: "openai-completions",
models: TOKEN_PLAN_MODEL_CATALOG.map(buildTokenPlanModelDefinition),
};
}

View File

@@ -0,0 +1,16 @@
{
"extends": "../tsconfig.package-boundary.base.json",
"compilerOptions": {
"rootDir": "."
},
"include": ["./*.ts", "./src/**/*.ts"],
"exclude": [
"./**/*.test.ts",
"./dist/**",
"./node_modules/**",
"./src/test-support/**",
"./src/**/*test-helpers.ts",
"./src/**/*test-harness.ts",
"./src/**/*test-support.ts"
]
}

6
pnpm-lock.yaml generated
View File

@@ -1182,6 +1182,12 @@ importers:
specifier: workspace:*
version: link:../../packages/plugin-sdk
extensions/tencent:
devDependencies:
'@openclaw/plugin-sdk':
specifier: workspace:*
version: link:../../packages/plugin-sdk
extensions/tlon:
dependencies:
'@aws-sdk/client-s3':