Merge bfb27e8106 into bc432d8435
This commit is contained in:
commit
990c0cf3f0
@ -46,6 +46,7 @@ See [Venice AI](/providers/venice).
|
||||
- [GLM models](/providers/glm)
|
||||
- [MiniMax](/providers/minimax)
|
||||
- [Venius (Venice AI, privacy-focused)](/providers/venice)
|
||||
- [PixelML (multi-model API)](/providers/pixelml)
|
||||
- [Ollama (local models)](/providers/ollama)
|
||||
|
||||
## Transcription providers
|
||||
|
||||
141
docs/providers/pixelml.md
Normal file
141
docs/providers/pixelml.md
Normal file
@ -0,0 +1,141 @@
|
||||
---
|
||||
summary: "Use PixelML multi-model API in Clawdbot"
|
||||
read_when:
|
||||
- You want to use PixelML models in Clawdbot
|
||||
- You need PixelML setup guidance
|
||||
---
|
||||
# PixelML
|
||||
|
||||
PixelML provides a unified OpenAI-compatible API for accessing multiple AI models including GPT-4o, Claude, and more.
|
||||
|
||||
## Features
|
||||
|
||||
- **Multi-model access**: Use GPT, Claude, and other models through a single API
|
||||
- **OpenAI-compatible**: Standard `/v1` endpoints for easy integration
|
||||
- **Streaming**: Supported on all models
|
||||
- **Vision**: Supported on models with vision capability
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Get API Key
|
||||
|
||||
1. Sign up at [platform.pixelml.com](https://platform.pixelml.com)
|
||||
2. Go to your account settings and create an API key
|
||||
3. Copy your API key
|
||||
|
||||
### 2. Configure Clawdbot
|
||||
|
||||
**Option A: Environment Variable**
|
||||
|
||||
```bash
|
||||
export PIXELML_API_KEY="your-api-key"
|
||||
```
|
||||
|
||||
**Option B: Interactive Setup (Recommended)**
|
||||
|
||||
```bash
|
||||
clawdbot onboard --auth-choice pixelml-api-key
|
||||
```
|
||||
|
||||
This will:
|
||||
1. Prompt for your API key (or use existing `PIXELML_API_KEY`)
|
||||
2. Configure the provider automatically
|
||||
3. Set the default model
|
||||
|
||||
**Option C: Non-interactive**
|
||||
|
||||
```bash
|
||||
clawdbot onboard --non-interactive \
|
||||
--auth-choice pixelml-api-key \
|
||||
--pixelml-api-key "your-api-key"
|
||||
```
|
||||
|
||||
### 3. Verify Setup
|
||||
|
||||
```bash
|
||||
clawdbot chat --model pixelml/gpt-4o-mini "Hello, are you working?"
|
||||
```
|
||||
|
||||
## Available Models
|
||||
|
||||
| Model ID | Name | Context | Features |
|
||||
|----------|------|---------|----------|
|
||||
| `gpt-4o-mini` | GPT-4o Mini | 128k | Vision |
|
||||
| `gpt-4o` | GPT-4o | 128k | Vision |
|
||||
| `claude-4.5-haiku` | Claude 4.5 Haiku | 200k | Vision |
|
||||
| `claude-4.5-sonnet` | Claude 4.5 Sonnet | 200k | Vision, Reasoning |
|
||||
|
||||
## Model Selection
|
||||
|
||||
Change your default model anytime:
|
||||
|
||||
```bash
|
||||
clawdbot models set pixelml/gpt-4o-mini
|
||||
clawdbot models set pixelml/claude-4.5-sonnet
|
||||
```
|
||||
|
||||
List all available models:
|
||||
|
||||
```bash
|
||||
clawdbot models list | grep pixelml
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Use GPT-4o Mini (default)
|
||||
clawdbot chat --model pixelml/gpt-4o-mini "Hello"
|
||||
|
||||
# Use Claude via PixelML
|
||||
clawdbot chat --model pixelml/claude-4.5-sonnet "Write a poem"
|
||||
|
||||
# Use GPT-4o for vision tasks
|
||||
clawdbot chat --model pixelml/gpt-4o
|
||||
```
|
||||
|
||||
## Config File Example
|
||||
|
||||
```json5
|
||||
{
|
||||
env: { PIXELML_API_KEY: "your-api-key" },
|
||||
agents: { defaults: { model: { primary: "pixelml/gpt-4o-mini" } } },
|
||||
models: {
|
||||
mode: "merge",
|
||||
providers: {
|
||||
pixelml: {
|
||||
baseUrl: "https://ishi.pixelml.com/v1",
|
||||
apiKey: "${PIXELML_API_KEY}",
|
||||
api: "openai-completions",
|
||||
models: [
|
||||
{
|
||||
id: "gpt-4o-mini",
|
||||
name: "GPT-4o Mini",
|
||||
reasoning: false,
|
||||
input: ["text", "image"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 128000,
|
||||
maxTokens: 16384
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### API key not recognized
|
||||
|
||||
```bash
|
||||
echo $PIXELML_API_KEY
|
||||
clawdbot models list | grep pixelml
|
||||
```
|
||||
|
||||
### Connection issues
|
||||
|
||||
PixelML API is at `https://ishi.pixelml.com/v1`. Ensure your network allows HTTPS connections.
|
||||
|
||||
## Links
|
||||
|
||||
- [PixelML](https://platform.pixelml.com)
|
||||
@ -284,6 +284,7 @@ export function resolveEnvApiKey(provider: string): EnvApiKeyResult | null {
|
||||
xiaomi: "XIAOMI_API_KEY",
|
||||
synthetic: "SYNTHETIC_API_KEY",
|
||||
venice: "VENICE_API_KEY",
|
||||
pixelml: "PIXELML_API_KEY",
|
||||
mistral: "MISTRAL_API_KEY",
|
||||
opencode: "OPENCODE_API_KEY",
|
||||
};
|
||||
|
||||
@ -12,6 +12,7 @@ import {
|
||||
SYNTHETIC_BASE_URL,
|
||||
SYNTHETIC_MODEL_CATALOG,
|
||||
} from "./synthetic-models.js";
|
||||
import { discoverPixelmlModels, PIXELML_BASE_URL } from "./pixelml-models.js";
|
||||
import { discoverVeniceModels, VENICE_BASE_URL } from "./venice-models.js";
|
||||
|
||||
type ModelsConfig = NonNullable<OpenClawConfig["models"]>;
|
||||
@ -379,6 +380,15 @@ async function buildVeniceProvider(): Promise<ProviderConfig> {
|
||||
};
|
||||
}
|
||||
|
||||
async function buildPixelmlProvider(apiKey?: string): Promise<ProviderConfig> {
|
||||
const models = await discoverPixelmlModels(apiKey);
|
||||
return {
|
||||
baseUrl: PIXELML_BASE_URL,
|
||||
api: "openai-completions",
|
||||
models,
|
||||
};
|
||||
}
|
||||
|
||||
async function buildOllamaProvider(): Promise<ProviderConfig> {
|
||||
const models = await discoverOllamaModels();
|
||||
return {
|
||||
@ -431,6 +441,13 @@ export async function resolveImplicitProviders(params: {
|
||||
providers.venice = { ...(await buildVeniceProvider()), apiKey: veniceKey };
|
||||
}
|
||||
|
||||
const pixelmlKey =
|
||||
resolveEnvApiKeyVarName("pixelml") ??
|
||||
resolveApiKeyFromProfiles({ provider: "pixelml", store: authStore });
|
||||
if (pixelmlKey) {
|
||||
providers.pixelml = { ...(await buildPixelmlProvider(pixelmlKey)), apiKey: pixelmlKey };
|
||||
}
|
||||
|
||||
const qwenProfiles = listProfilesForProvider(authStore, "qwen-portal");
|
||||
if (qwenProfiles.length > 0) {
|
||||
providers["qwen-portal"] = {
|
||||
|
||||
188
src/agents/pixelml-models.ts
Normal file
188
src/agents/pixelml-models.ts
Normal file
@ -0,0 +1,188 @@
|
||||
import type { ModelDefinitionConfig } from "../config/types.js";
|
||||
|
||||
export const PIXELML_BASE_URL = "https://ishi.pixelml.com/v1";
|
||||
export const PIXELML_DEFAULT_MODEL_ID = "gpt-4o-mini";
|
||||
export const PIXELML_DEFAULT_MODEL_REF = `pixelml/${PIXELML_DEFAULT_MODEL_ID}`;
|
||||
|
||||
// PixelML uses a unified pricing model; set to 0 as costs vary by model.
|
||||
export const PIXELML_DEFAULT_COST = {
|
||||
input: 0,
|
||||
output: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* Static catalog of PixelML models.
|
||||
* PixelML provides a unified OpenAI-compatible API for multiple AI models.
|
||||
*
|
||||
* This catalog serves as a fallback when the PixelML API is unreachable.
|
||||
*/
|
||||
export const PIXELML_MODEL_CATALOG = [
|
||||
// GPT models
|
||||
{
|
||||
id: "gpt-4o-mini",
|
||||
name: "GPT-4o Mini",
|
||||
reasoning: false,
|
||||
input: ["text", "image"],
|
||||
contextWindow: 128000,
|
||||
maxTokens: 16384,
|
||||
},
|
||||
{
|
||||
id: "gpt-4o",
|
||||
name: "GPT-4o",
|
||||
reasoning: false,
|
||||
input: ["text", "image"],
|
||||
contextWindow: 128000,
|
||||
maxTokens: 16384,
|
||||
},
|
||||
// Claude models
|
||||
{
|
||||
id: "claude-4.5-haiku",
|
||||
name: "Claude 4.5 Haiku",
|
||||
reasoning: false,
|
||||
input: ["text", "image"],
|
||||
contextWindow: 200000,
|
||||
maxTokens: 8192,
|
||||
},
|
||||
{
|
||||
id: "claude-4.5-sonnet",
|
||||
name: "Claude 4.5 Sonnet",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
contextWindow: 200000,
|
||||
maxTokens: 8192,
|
||||
},
|
||||
] as const;
|
||||
|
||||
export type PixelmlCatalogEntry = (typeof PIXELML_MODEL_CATALOG)[number];
|
||||
|
||||
/**
|
||||
* Build a ModelDefinitionConfig from a PixelML catalog entry.
|
||||
*/
|
||||
export function buildPixelmlModelDefinition(entry: PixelmlCatalogEntry): ModelDefinitionConfig {
|
||||
return {
|
||||
id: entry.id,
|
||||
name: entry.name,
|
||||
reasoning: entry.reasoning,
|
||||
input: [...entry.input],
|
||||
cost: PIXELML_DEFAULT_COST,
|
||||
contextWindow: entry.contextWindow,
|
||||
maxTokens: entry.maxTokens,
|
||||
};
|
||||
}
|
||||
|
||||
// PixelML API response types
|
||||
interface PixelmlModelCost {
|
||||
input: number;
|
||||
output: number;
|
||||
}
|
||||
|
||||
interface PixelmlModelLimit {
|
||||
context: number;
|
||||
output: number;
|
||||
}
|
||||
|
||||
interface PixelmlModelModalities {
|
||||
input: Array<"text" | "image">;
|
||||
output: Array<"text" | "image">;
|
||||
}
|
||||
|
||||
interface PixelmlModel {
|
||||
id: string;
|
||||
name: string;
|
||||
family?: string;
|
||||
release_date?: string;
|
||||
attachment?: boolean;
|
||||
reasoning?: boolean;
|
||||
temperature?: boolean;
|
||||
tool_call?: boolean;
|
||||
cost?: PixelmlModelCost;
|
||||
limit?: PixelmlModelLimit;
|
||||
modalities?: PixelmlModelModalities;
|
||||
options?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
// Response can be either { data: [...] } or a flat array
|
||||
type PixelmlModelsResponse = { data: PixelmlModel[] } | PixelmlModel[];
|
||||
|
||||
/**
|
||||
* Discover models from PixelML API with fallback to static catalog.
|
||||
* The /models endpoint requires authentication.
|
||||
*/
|
||||
export async function discoverPixelmlModels(apiKey?: string): Promise<ModelDefinitionConfig[]> {
|
||||
// Skip API discovery in test environment
|
||||
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
||||
return PIXELML_MODEL_CATALOG.map(buildPixelmlModelDefinition);
|
||||
}
|
||||
|
||||
// Without an API key, return the static catalog
|
||||
if (!apiKey?.trim()) {
|
||||
return PIXELML_MODEL_CATALOG.map(buildPixelmlModelDefinition);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${PIXELML_BASE_URL}/models`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
signal: AbortSignal.timeout(5000),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn(
|
||||
`[pixelml-models] Failed to discover models: HTTP ${response.status}, using static catalog`,
|
||||
);
|
||||
return PIXELML_MODEL_CATALOG.map(buildPixelmlModelDefinition);
|
||||
}
|
||||
|
||||
const rawData = (await response.json()) as PixelmlModelsResponse;
|
||||
// Handle both { data: [...] } and flat array responses
|
||||
const apiModels = Array.isArray(rawData) ? rawData : rawData.data;
|
||||
if (!Array.isArray(apiModels) || apiModels.length === 0) {
|
||||
console.warn("[pixelml-models] No models found from API, using static catalog");
|
||||
return PIXELML_MODEL_CATALOG.map(buildPixelmlModelDefinition);
|
||||
}
|
||||
|
||||
// Build models from API response
|
||||
const models: ModelDefinitionConfig[] = [];
|
||||
|
||||
for (const apiModel of apiModels) {
|
||||
// Extract model ID without provider prefix (e.g., "pixelml/gpt-5.1" -> "gpt-5.1")
|
||||
const modelId = apiModel.id.includes("/")
|
||||
? apiModel.id.split("/").slice(1).join("/")
|
||||
: apiModel.id;
|
||||
|
||||
// Use API-provided modalities, filtering to only supported types (text, image)
|
||||
const rawModalities = apiModel.modalities?.input ?? ["text"];
|
||||
const inputModalities: Array<"text" | "image"> = rawModalities.filter(
|
||||
(m): m is "text" | "image" => m === "text" || m === "image",
|
||||
);
|
||||
// Ensure at least "text" is present
|
||||
if (inputModalities.length === 0) {
|
||||
inputModalities.push("text");
|
||||
}
|
||||
|
||||
// Use API-provided values with sensible defaults
|
||||
models.push({
|
||||
id: modelId,
|
||||
name: apiModel.name || modelId,
|
||||
reasoning: apiModel.reasoning ?? false,
|
||||
input: inputModalities,
|
||||
cost: {
|
||||
input: apiModel.cost?.input ?? 0,
|
||||
output: apiModel.cost?.output ?? 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: apiModel.limit?.context ?? 128000,
|
||||
maxTokens: apiModel.limit?.output ?? 8192,
|
||||
});
|
||||
}
|
||||
|
||||
return models.length > 0 ? models : PIXELML_MODEL_CATALOG.map(buildPixelmlModelDefinition);
|
||||
} catch (error) {
|
||||
console.warn(`[pixelml-models] Discovery failed: ${String(error)}, using static catalog`);
|
||||
return PIXELML_MODEL_CATALOG.map(buildPixelmlModelDefinition);
|
||||
}
|
||||
}
|
||||
@ -21,6 +21,7 @@ export type AuthChoiceGroupId =
|
||||
| "minimax"
|
||||
| "synthetic"
|
||||
| "venice"
|
||||
| "pixelml"
|
||||
| "qwen";
|
||||
|
||||
export type AuthChoiceGroup = {
|
||||
@ -72,6 +73,12 @@ const AUTH_CHOICE_GROUP_DEFS: {
|
||||
hint: "Privacy-focused (uncensored models)",
|
||||
choices: ["venice-api-key"],
|
||||
},
|
||||
{
|
||||
value: "pixelml",
|
||||
label: "PixelML",
|
||||
hint: "Multi-model API (GPT, Claude)",
|
||||
choices: ["pixelml-api-key"],
|
||||
},
|
||||
{
|
||||
value: "google",
|
||||
label: "Google",
|
||||
@ -154,6 +161,11 @@ export function buildAuthChoiceOptions(params: {
|
||||
label: "Venice AI API key",
|
||||
hint: "Privacy-focused inference (uncensored models)",
|
||||
});
|
||||
options.push({
|
||||
value: "pixelml-api-key",
|
||||
label: "PixelML API key",
|
||||
hint: "Multi-model API (GPT, Claude via PixelML)",
|
||||
});
|
||||
options.push({
|
||||
value: "github-copilot",
|
||||
label: "GitHub Copilot (GitHub device login)",
|
||||
|
||||
@ -21,6 +21,8 @@ import {
|
||||
applyOpencodeZenProviderConfig,
|
||||
applyOpenrouterConfig,
|
||||
applyOpenrouterProviderConfig,
|
||||
applyPixelmlConfig,
|
||||
applyPixelmlProviderConfig,
|
||||
applySyntheticConfig,
|
||||
applySyntheticProviderConfig,
|
||||
applyVeniceConfig,
|
||||
@ -33,6 +35,7 @@ import {
|
||||
KIMI_CODE_MODEL_REF,
|
||||
MOONSHOT_DEFAULT_MODEL_REF,
|
||||
OPENROUTER_DEFAULT_MODEL_REF,
|
||||
PIXELML_DEFAULT_MODEL_REF,
|
||||
SYNTHETIC_DEFAULT_MODEL_REF,
|
||||
VENICE_DEFAULT_MODEL_REF,
|
||||
VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF,
|
||||
@ -42,6 +45,7 @@ import {
|
||||
setMoonshotApiKey,
|
||||
setOpencodeZenApiKey,
|
||||
setOpenrouterApiKey,
|
||||
setPixelmlApiKey,
|
||||
setSyntheticApiKey,
|
||||
setVeniceApiKey,
|
||||
setVercelAiGatewayApiKey,
|
||||
@ -89,6 +93,8 @@ export async function applyAuthChoiceApiProviders(
|
||||
authChoice = "synthetic-api-key";
|
||||
} else if (params.opts.tokenProvider === "venice") {
|
||||
authChoice = "venice-api-key";
|
||||
} else if (params.opts.tokenProvider === "pixelml") {
|
||||
authChoice = "pixelml-api-key";
|
||||
} else if (params.opts.tokenProvider === "opencode") {
|
||||
authChoice = "opencode-zen";
|
||||
}
|
||||
@ -576,6 +582,64 @@ export async function applyAuthChoiceApiProviders(
|
||||
return { config: nextConfig, agentModelOverride };
|
||||
}
|
||||
|
||||
if (authChoice === "pixelml-api-key") {
|
||||
let hasCredential = false;
|
||||
|
||||
if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "pixelml") {
|
||||
await setPixelmlApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir);
|
||||
hasCredential = true;
|
||||
}
|
||||
|
||||
if (!hasCredential) {
|
||||
await params.prompter.note(
|
||||
[
|
||||
"PixelML provides access to multiple AI models (GPT, Claude, etc.).",
|
||||
"Get your API key at: https://platform.pixelml.com",
|
||||
].join("\n"),
|
||||
"PixelML",
|
||||
);
|
||||
}
|
||||
|
||||
const envKey = resolveEnvApiKey("pixelml");
|
||||
if (envKey) {
|
||||
const useExisting = await params.prompter.confirm({
|
||||
message: `Use existing PIXELML_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
|
||||
initialValue: true,
|
||||
});
|
||||
if (useExisting) {
|
||||
await setPixelmlApiKey(envKey.apiKey, params.agentDir);
|
||||
hasCredential = true;
|
||||
}
|
||||
}
|
||||
if (!hasCredential) {
|
||||
const key = await params.prompter.text({
|
||||
message: "Enter PixelML API key",
|
||||
validate: validateApiKeyInput,
|
||||
});
|
||||
await setPixelmlApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
|
||||
}
|
||||
nextConfig = applyAuthProfileConfig(nextConfig, {
|
||||
profileId: "pixelml:default",
|
||||
provider: "pixelml",
|
||||
mode: "api_key",
|
||||
});
|
||||
{
|
||||
const applied = await applyDefaultModelChoice({
|
||||
config: nextConfig,
|
||||
setDefaultModel: params.setDefaultModel,
|
||||
defaultModel: PIXELML_DEFAULT_MODEL_REF,
|
||||
applyDefaultConfig: applyPixelmlConfig,
|
||||
applyProviderConfig: applyPixelmlProviderConfig,
|
||||
noteDefault: PIXELML_DEFAULT_MODEL_REF,
|
||||
noteAgentModel,
|
||||
prompter: params.prompter,
|
||||
});
|
||||
nextConfig = applied.config;
|
||||
agentModelOverride = applied.agentModelOverride ?? agentModelOverride;
|
||||
}
|
||||
return { config: nextConfig, agentModelOverride };
|
||||
}
|
||||
|
||||
if (authChoice === "opencode-zen") {
|
||||
let hasCredential = false;
|
||||
if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "opencode") {
|
||||
|
||||
@ -21,6 +21,7 @@ const PREFERRED_PROVIDER_BY_AUTH_CHOICE: Partial<Record<AuthChoice, string>> = {
|
||||
"xiaomi-api-key": "xiaomi",
|
||||
"synthetic-api-key": "synthetic",
|
||||
"venice-api-key": "venice",
|
||||
"pixelml-api-key": "pixelml",
|
||||
"github-copilot": "github-copilot",
|
||||
"copilot-proxy": "copilot-proxy",
|
||||
"minimax-cloud": "minimax",
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import { buildXiaomiProvider, XIAOMI_DEFAULT_MODEL_ID } from "../agents/models-config.providers.js";
|
||||
import {
|
||||
buildPixelmlModelDefinition,
|
||||
PIXELML_BASE_URL,
|
||||
PIXELML_DEFAULT_MODEL_REF,
|
||||
PIXELML_MODEL_CATALOG,
|
||||
} from "../agents/pixelml-models.js";
|
||||
import {
|
||||
buildSyntheticModelDefinition,
|
||||
SYNTHETIC_BASE_URL,
|
||||
@ -484,6 +490,91 @@ export function applyVeniceConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply PixelML provider configuration without changing the default model.
|
||||
* Registers PixelML models and sets up the provider, but preserves existing model selection.
|
||||
*/
|
||||
export function applyPixelmlProviderConfig(cfg: ClawdbotConfig): ClawdbotConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
// Add all catalog models to the allowlist
|
||||
for (const entry of PIXELML_MODEL_CATALOG) {
|
||||
const modelRef = `pixelml/${entry.id}`;
|
||||
if (!models[modelRef]) {
|
||||
models[modelRef] = {};
|
||||
}
|
||||
}
|
||||
// Set alias for the default model
|
||||
models[PIXELML_DEFAULT_MODEL_REF] = {
|
||||
...models[PIXELML_DEFAULT_MODEL_REF],
|
||||
alias: models[PIXELML_DEFAULT_MODEL_REF]?.alias ?? "GPT-4o Mini",
|
||||
};
|
||||
|
||||
const providers = { ...cfg.models?.providers };
|
||||
const existingProvider = providers.pixelml;
|
||||
const existingModels = Array.isArray(existingProvider?.models) ? existingProvider.models : [];
|
||||
const pixelmlModels = PIXELML_MODEL_CATALOG.map(buildPixelmlModelDefinition);
|
||||
const mergedModels = [
|
||||
...existingModels,
|
||||
...pixelmlModels.filter(
|
||||
(model) => !existingModels.some((existing) => existing.id === model.id),
|
||||
),
|
||||
];
|
||||
const { apiKey: existingApiKey, ...existingProviderRest } = (existingProvider ?? {}) as Record<
|
||||
string,
|
||||
unknown
|
||||
> as { apiKey?: string };
|
||||
const resolvedApiKey = typeof existingApiKey === "string" ? existingApiKey : undefined;
|
||||
const normalizedApiKey = resolvedApiKey?.trim();
|
||||
providers.pixelml = {
|
||||
...existingProviderRest,
|
||||
baseUrl: PIXELML_BASE_URL,
|
||||
api: "openai-completions",
|
||||
...(normalizedApiKey ? { apiKey: normalizedApiKey } : {}),
|
||||
models: mergedModels.length > 0 ? mergedModels : pixelmlModels,
|
||||
};
|
||||
|
||||
return {
|
||||
...cfg,
|
||||
agents: {
|
||||
...cfg.agents,
|
||||
defaults: {
|
||||
...cfg.agents?.defaults,
|
||||
models,
|
||||
},
|
||||
},
|
||||
models: {
|
||||
mode: cfg.models?.mode ?? "merge",
|
||||
providers,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply PixelML provider configuration AND set PixelML as the default model.
|
||||
* Use this when PixelML is the primary provider choice during onboarding.
|
||||
*/
|
||||
export function applyPixelmlConfig(cfg: ClawdbotConfig): ClawdbotConfig {
|
||||
const next = applyPixelmlProviderConfig(cfg);
|
||||
const existingModel = next.agents?.defaults?.model;
|
||||
return {
|
||||
...next,
|
||||
agents: {
|
||||
...next.agents,
|
||||
defaults: {
|
||||
...next.agents?.defaults,
|
||||
model: {
|
||||
...(existingModel && "fallbacks" in (existingModel as Record<string, unknown>)
|
||||
? {
|
||||
fallbacks: (existingModel as { fallbacks?: string[] }).fallbacks,
|
||||
}
|
||||
: undefined),
|
||||
primary: PIXELML_DEFAULT_MODEL_REF,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function applyAuthProfileConfig(
|
||||
cfg: OpenClawConfig,
|
||||
params: {
|
||||
|
||||
@ -112,6 +112,19 @@ export async function setVeniceApiKey(key: string, agentDir?: string) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function setPixelmlApiKey(key: string, agentDir?: string) {
|
||||
// Write to resolved agent dir so gateway finds credentials on startup.
|
||||
upsertAuthProfile({
|
||||
profileId: "pixelml:default",
|
||||
credential: {
|
||||
type: "api_key",
|
||||
provider: "pixelml",
|
||||
key,
|
||||
},
|
||||
agentDir: resolveAuthAgentDir(agentDir),
|
||||
});
|
||||
}
|
||||
|
||||
export const ZAI_DEFAULT_MODEL_REF = "zai/glm-4.7";
|
||||
export const XIAOMI_DEFAULT_MODEL_REF = "xiaomi/mimo-v2-flash";
|
||||
export const OPENROUTER_DEFAULT_MODEL_REF = "openrouter/auto";
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
export { PIXELML_DEFAULT_MODEL_ID, PIXELML_DEFAULT_MODEL_REF } from "../agents/pixelml-models.js";
|
||||
export {
|
||||
SYNTHETIC_DEFAULT_MODEL_ID,
|
||||
SYNTHETIC_DEFAULT_MODEL_REF,
|
||||
@ -11,6 +12,8 @@ export {
|
||||
applyMoonshotProviderConfig,
|
||||
applyOpenrouterConfig,
|
||||
applyOpenrouterProviderConfig,
|
||||
applyPixelmlConfig,
|
||||
applyPixelmlProviderConfig,
|
||||
applySyntheticConfig,
|
||||
applySyntheticProviderConfig,
|
||||
applyVeniceConfig,
|
||||
@ -43,6 +46,7 @@ export {
|
||||
setMoonshotApiKey,
|
||||
setOpencodeZenApiKey,
|
||||
setOpenrouterApiKey,
|
||||
setPixelmlApiKey,
|
||||
setSyntheticApiKey,
|
||||
setVeniceApiKey,
|
||||
setVercelAiGatewayApiKey,
|
||||
|
||||
@ -17,6 +17,7 @@ export type AuthChoice =
|
||||
| "kimi-code-api-key"
|
||||
| "synthetic-api-key"
|
||||
| "venice-api-key"
|
||||
| "pixelml-api-key"
|
||||
| "codex-cli"
|
||||
| "apiKey"
|
||||
| "gemini-api-key"
|
||||
@ -72,6 +73,7 @@ export type OnboardOptions = {
|
||||
minimaxApiKey?: string;
|
||||
syntheticApiKey?: string;
|
||||
veniceApiKey?: string;
|
||||
pixelmlApiKey?: string;
|
||||
opencodeZenApiKey?: string;
|
||||
gatewayPort?: number;
|
||||
gatewayBind?: GatewayBind;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user