Files
Neon-Desk/lib/server/openclaw.ts

93 lines
2.3 KiB
TypeScript

type ChatCompletionResponse = {
choices?: Array<{
message?: {
content?: string;
};
}>;
};
function envValue(name: string) {
const value = process.env[name];
if (!value) {
return undefined;
}
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : undefined;
}
const DEFAULT_MODEL = 'zeroclaw';
function fallbackResponse(prompt: string) {
const clipped = prompt.split('\n').slice(0, 6).join(' ').slice(0, 260);
return [
'OpenClaw fallback mode is active (missing OPENCLAW_BASE_URL or OPENCLAW_API_KEY).',
'Thesis: Portfolio remains analyzable with local heuristics until live model access is configured.',
'Risk scan: Concentration and filing sentiment should be monitored after each sync cycle.',
`Context digest: ${clipped}`
].join('\n\n');
}
export function getOpenClawConfig() {
return {
baseUrl: envValue('OPENCLAW_BASE_URL'),
apiKey: envValue('OPENCLAW_API_KEY'),
model: envValue('OPENCLAW_MODEL') ?? DEFAULT_MODEL
};
}
export function isOpenClawConfigured() {
const config = getOpenClawConfig();
return Boolean(config.baseUrl && config.apiKey);
}
export async function runOpenClawAnalysis(prompt: string, systemPrompt?: string) {
const config = getOpenClawConfig();
if (!config.baseUrl || !config.apiKey) {
return {
provider: 'local-fallback',
model: config.model,
text: fallbackResponse(prompt)
};
}
const response = await fetch(`${config.baseUrl}/v1/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${config.apiKey}`
},
body: JSON.stringify({
model: config.model,
temperature: 0.2,
messages: [
systemPrompt
? { role: 'system', content: systemPrompt }
: null,
{ role: 'user', content: prompt }
].filter(Boolean)
}),
cache: 'no-store'
});
if (!response.ok) {
const body = await response.text();
throw new Error(`OpenClaw request failed (${response.status}): ${body.slice(0, 220)}`);
}
const payload = await response.json() as ChatCompletionResponse;
const text = payload.choices?.[0]?.message?.content?.trim();
if (!text) {
throw new Error('OpenClaw returned an empty response');
}
return {
provider: 'openclaw',
model: config.model,
text
};
}