93 lines
2.3 KiB
TypeScript
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
|
|
};
|
|
}
|