rebuild app as turbopack-first single-stack with internal api and openclaw tasks
This commit is contained in:
92
frontend/lib/server/openclaw.ts
Normal file
92
frontend/lib/server/openclaw.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
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
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user