mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-30 23:14:37 +00:00
Enhance development scripts and improve tool documentation
- Updated `package.json` scripts for better error handling and streamlined execution of Tauri commands. - Added a new `gmail-skill-integration.md` document detailing the Gmail skill integration process, including OAuth2 flow and tool definitions. - Improved formatting and consistency in `TOOLS.md` for better readability and clarity. - Enhanced logging in skill management and transport layers for better debugging and monitoring of tool calls.
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import type { OAuthProviderConfig } from '../../types/oauth';
|
||||
import { IS_DEV } from '../../utils/config';
|
||||
import { openUrl } from '../../utils/openUrl';
|
||||
import { isTauri } from '../../utils/tauriCommands';
|
||||
import { IS_DEV } from '../../utils/config';
|
||||
|
||||
interface OAuthProviderButtonProps {
|
||||
provider: OAuthProviderConfig;
|
||||
@@ -25,7 +26,9 @@ const OAuthProviderButton = ({
|
||||
if (IS_DEV) {
|
||||
console.log(`[dev] OAuth debug mode enabled. OAuth URL: ${provider.loginUrl}`);
|
||||
console.log('[dev] In debug mode, OAuth will return JSON response instead of redirect.');
|
||||
console.log('[dev] After OAuth completion, copy the loginToken and use: window.__simulateDeepLink("alphahuman://auth?token=YOUR_TOKEN")');
|
||||
console.log(
|
||||
'[dev] After OAuth completion, copy the loginToken and use: window.__simulateDeepLink("alphahuman://auth?token=YOUR_TOKEN")'
|
||||
);
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
@@ -51,18 +54,17 @@ const OAuthProviderButton = ({
|
||||
<button
|
||||
onClick={handleOAuthLogin}
|
||||
disabled={isDisabled}
|
||||
className={`w-full flex items-center justify-center space-x-3 ${provider.color} ${provider.hoverColor} ${provider.textColor} font-semibold py-4 rounded-xl transition-all duration-300 hover:shadow-medium hover:scale-[1.02] active:scale-[0.98] disabled:hover:scale-100 disabled:opacity-50 disabled:cursor-not-allowed ${className}`}
|
||||
>
|
||||
className={`w-full flex items-center justify-center space-x-3 ${provider.color} ${provider.hoverColor} font-semibold py-4 rounded-xl transition-all duration-300 hover:shadow-medium hover:scale-[1.02] active:scale-[0.98] disabled:hover:scale-100 disabled:opacity-50 disabled:cursor-not-allowed ${className}`}>
|
||||
{isLoading ? (
|
||||
<div className="animate-spin rounded-full h-5 w-5 border-b-2 border-current"></div>
|
||||
) : (
|
||||
<IconComponent className="w-5 h-5" />
|
||||
)}
|
||||
<span>
|
||||
<span className={provider.textColor}>
|
||||
{isLoading ? 'Connecting...' : `Continue with ${provider.name}`}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default OAuthProviderButton;
|
||||
export default OAuthProviderButton;
|
||||
|
||||
@@ -7,28 +7,40 @@ import { BACKEND_URL, IS_DEV } from '../../utils/config';
|
||||
// Provider Icons
|
||||
const GoogleIcon = ({ className = '' }: { className?: string }) => (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/>
|
||||
<path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/>
|
||||
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/>
|
||||
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
|
||||
<path
|
||||
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
||||
fill="#4285F4"
|
||||
/>
|
||||
<path
|
||||
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
||||
fill="#34A853"
|
||||
/>
|
||||
<path
|
||||
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
||||
fill="#FBBC05"
|
||||
/>
|
||||
<path
|
||||
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
||||
fill="#EA4335"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const TwitterIcon = ({ className = '' }: { className?: string }) => (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const GitHubIcon = ({ className = '' }: { className?: string }) => (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const DiscordIcon = ({ className = '' }: { className?: string }) => (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419-.0189 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1568 2.4189Z"/>
|
||||
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419-.0189 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1568 2.4189Z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
@@ -73,4 +85,4 @@ export const oauthProviderConfigs: OAuthProviderConfig[] = [
|
||||
|
||||
export const getProviderConfig = (provider: string): OAuthProviderConfig | undefined => {
|
||||
return oauthProviderConfigs.find(config => config.id === provider);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -226,11 +226,15 @@ class SkillManager {
|
||||
name: string,
|
||||
args: Record<string, unknown>,
|
||||
): Promise<{ content: Array<{ type: string; text: string }>; isError: boolean }> {
|
||||
console.log(`[SkillManager] callTool skill="${skillId}" tool="${name}"`);
|
||||
const runtime = this.runtimes.get(skillId);
|
||||
if (!runtime) {
|
||||
console.error(`[SkillManager] callTool failed — skill "${skillId}" has no running runtime`);
|
||||
throw new Error(`Skill ${skillId} is not running`);
|
||||
}
|
||||
return runtime.callTool(name, args);
|
||||
const result = await runtime.callTool(name, args);
|
||||
console.log(`[SkillManager] callTool result skill="${skillId}" tool="${name}" isError=${result.isError}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -141,7 +141,10 @@ export class SkillRuntime {
|
||||
content: Array<{ type: string; text: string }>;
|
||||
isError: boolean;
|
||||
}> {
|
||||
return this.transport.request("tools/call", { name, arguments: args });
|
||||
console.log(`[SkillRuntime] callTool skill="${this.manifest.id}" tool="${name}"`);
|
||||
const result = await this.transport.request<{ content: Array<{ type: string; text: string }>; isError: boolean }>("tools/call", { name, arguments: args });
|
||||
console.log(`[SkillRuntime] tools/call response skill="${this.manifest.id}" tool="${name}" isError=${result.isError}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,11 +51,7 @@ export class SkillTransport {
|
||||
throw new Error("Skill transport not started");
|
||||
}
|
||||
|
||||
console.log("[skill-transport] Sending request", {
|
||||
skillId: this.skillId,
|
||||
method,
|
||||
hasParams: params !== undefined,
|
||||
});
|
||||
console.log("[skill-transport] →", { skillId: this.skillId, method, params });
|
||||
|
||||
const result = await invoke<T>("runtime_rpc", {
|
||||
skillId: this.skillId,
|
||||
@@ -63,10 +59,7 @@ export class SkillTransport {
|
||||
params: params ?? {},
|
||||
});
|
||||
|
||||
console.debug("[skill-transport] Received response", {
|
||||
skillId: this.skillId,
|
||||
method,
|
||||
});
|
||||
console.log("[skill-transport] ←", { skillId: this.skillId, method, result });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+110
-13
@@ -9,9 +9,15 @@ import {
|
||||
import Markdown from 'react-markdown';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { inferenceApi, type ModelInfo } from '../services/api/inferenceApi';
|
||||
import { injectAll } from '../lib/ai/injector';
|
||||
import type { Message } from '../lib/ai/providers/interface';
|
||||
import { skillManager } from '../lib/skills/manager';
|
||||
import {
|
||||
type ChatMessage,
|
||||
inferenceApi,
|
||||
type ModelInfo,
|
||||
type Tool,
|
||||
} from '../services/api/inferenceApi';
|
||||
import { useAppDispatch, useAppSelector } from '../store/hooks';
|
||||
import {
|
||||
addInferenceResponse,
|
||||
@@ -63,6 +69,8 @@ const Conversations = () => {
|
||||
isLoadingSuggestions,
|
||||
} = useAppSelector(state => state.thread);
|
||||
|
||||
const skillsState = useAppSelector(state => state.skills);
|
||||
|
||||
const [showPurgeConfirm, setShowPurgeConfirm] = useState(false);
|
||||
const [confirmDeleteId, setConfirmDeleteId] = useState<string | null>(null);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
@@ -255,14 +263,11 @@ const Conversations = () => {
|
||||
// Process user message with SOUL + TOOLS injection
|
||||
let processedUserContent = trimmed;
|
||||
try {
|
||||
const userMessage: Message = {
|
||||
role: 'user',
|
||||
content: [{ type: 'text', text: trimmed }]
|
||||
};
|
||||
const userMessage: Message = { role: 'user', content: [{ type: 'text', text: trimmed }] };
|
||||
|
||||
const injectedMessage = await injectAll(userMessage, {
|
||||
mode: 'context-block',
|
||||
includeMetadata: false
|
||||
includeMetadata: false,
|
||||
});
|
||||
|
||||
// Extract the processed text
|
||||
@@ -277,7 +282,7 @@ const Conversations = () => {
|
||||
// Continue with original message
|
||||
}
|
||||
|
||||
const chatMessages = [
|
||||
const chatMessages: ChatMessage[] = [
|
||||
...historySnapshot.map(m => ({
|
||||
role: (m.sender === 'user' ? 'user' : 'assistant') as 'user' | 'assistant',
|
||||
content: m.content,
|
||||
@@ -285,13 +290,105 @@ const Conversations = () => {
|
||||
{ role: 'user' as const, content: processedUserContent },
|
||||
];
|
||||
|
||||
const response = await inferenceApi.createChatCompletion({
|
||||
model: selectedModel,
|
||||
messages: chatMessages,
|
||||
});
|
||||
// Build tool definitions for ALL ready skills — namespaced as {skillId}__{toolName}
|
||||
const allSkillTools: Tool[] = Object.entries(skillsState.skills)
|
||||
.filter(([, skill]) => skill.status === 'ready' && skill.tools?.length)
|
||||
.flatMap(([skillId, skill]) =>
|
||||
(skill.tools ?? []).map(t => ({
|
||||
type: 'function' as const,
|
||||
function: {
|
||||
name: `${skillId}__${t.name}`,
|
||||
description: t.description,
|
||||
parameters: t.inputSchema as Tool['function']['parameters'],
|
||||
},
|
||||
}))
|
||||
);
|
||||
|
||||
const content = response.choices[0]?.message?.content ?? '';
|
||||
dispatch(addInferenceResponse({ content }));
|
||||
console.log(
|
||||
`[Conversations] active skill tools: ${allSkillTools.length}`,
|
||||
allSkillTools.map(t => t.function.name)
|
||||
);
|
||||
|
||||
// Agentic tool calling loop — handles multi-turn tool execution
|
||||
const loopMessages = [...chatMessages];
|
||||
let finalContent = '';
|
||||
const MAX_TOOL_ROUNDS = 5;
|
||||
|
||||
for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {
|
||||
const response = await inferenceApi.createChatCompletion({
|
||||
model: selectedModel,
|
||||
messages: loopMessages,
|
||||
...(allSkillTools.length > 0 ? { tools: allSkillTools, tool_choice: 'auto' } : {}),
|
||||
});
|
||||
console.log('🚀 ~ handleSendMessage ~ response:', response);
|
||||
|
||||
const choice = response.choices[0];
|
||||
if (!choice) break;
|
||||
|
||||
const { finish_reason, message } = choice;
|
||||
|
||||
if (finish_reason === 'tool_calls' && message.tool_calls?.length) {
|
||||
// Append assistant message with tool_calls
|
||||
loopMessages.push({
|
||||
role: 'assistant',
|
||||
content: message.content ?? '',
|
||||
tool_calls: message.tool_calls,
|
||||
});
|
||||
|
||||
// Execute each tool and collect results
|
||||
for (const tc of message.tool_calls) {
|
||||
const dunderIdx = tc.function.name.indexOf('__');
|
||||
const skillId = dunderIdx !== -1 ? tc.function.name.substring(0, dunderIdx) : '';
|
||||
const toolName =
|
||||
dunderIdx !== -1 ? tc.function.name.substring(dunderIdx + 2) : tc.function.name;
|
||||
|
||||
console.log(
|
||||
`[Conversations] tool_call dispatched — skill="${skillId}" tool="${toolName}" call_id="${tc.id}"`
|
||||
);
|
||||
|
||||
let toolResultContent = '';
|
||||
try {
|
||||
let toolArgs: Record<string, unknown> = {};
|
||||
try {
|
||||
toolArgs = JSON.parse(tc.function.arguments) as Record<string, unknown>;
|
||||
} catch {
|
||||
toolArgs = {};
|
||||
}
|
||||
console.log(
|
||||
`[Conversations] calling skillManager.callTool("${skillId}", "${toolName}")`,
|
||||
toolArgs
|
||||
);
|
||||
const result = await skillManager.callTool(skillId, toolName, toolArgs);
|
||||
toolResultContent = result.content.map(c => c.text).join('\n');
|
||||
if (result.isError) {
|
||||
console.warn(
|
||||
`[Conversations] tool "${toolName}" returned an error:`,
|
||||
toolResultContent
|
||||
);
|
||||
toolResultContent = `Error: ${toolResultContent}`;
|
||||
} else {
|
||||
console.log(
|
||||
`[Conversations] tool "${toolName}" succeeded:`,
|
||||
toolResultContent.slice(0, 200)
|
||||
);
|
||||
}
|
||||
} catch (toolErr) {
|
||||
console.error(`[Conversations] tool "${toolName}" threw:`, toolErr);
|
||||
toolResultContent = `Tool execution failed: ${toolErr instanceof Error ? toolErr.message : String(toolErr)}`;
|
||||
}
|
||||
|
||||
loopMessages.push({ role: 'tool', tool_call_id: tc.id, content: toolResultContent });
|
||||
}
|
||||
// Continue loop to get final response after tool results
|
||||
continue;
|
||||
}
|
||||
|
||||
// Normal (non-tool) response — done
|
||||
finalContent = message.content ?? '';
|
||||
break;
|
||||
}
|
||||
|
||||
dispatch(addInferenceResponse({ content: finalContent }));
|
||||
} catch (err) {
|
||||
dispatch(removeOptimisticMessages());
|
||||
const msg =
|
||||
|
||||
@@ -2,11 +2,41 @@ import { apiClient } from '../apiClient';
|
||||
|
||||
// ── Request types ────────────────────────────────────────────────────────────
|
||||
|
||||
export type ChatRole = 'system' | 'user' | 'assistant';
|
||||
export type ChatRole = 'system' | 'user' | 'assistant' | 'tool';
|
||||
|
||||
export interface ChatMessage {
|
||||
role: ChatRole;
|
||||
content: string;
|
||||
/** tool_call_id for role=tool messages */
|
||||
tool_call_id?: string;
|
||||
/** tool_calls emitted by the assistant */
|
||||
tool_calls?: ToolCall[];
|
||||
}
|
||||
|
||||
// ── Tool calling types (OpenAI-compatible) ───────────────────────────────────
|
||||
|
||||
export interface ToolFunction {
|
||||
name: string;
|
||||
description: string;
|
||||
parameters: {
|
||||
type: 'object';
|
||||
properties: Record<string, unknown>;
|
||||
required?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface Tool {
|
||||
type: 'function';
|
||||
function: ToolFunction;
|
||||
}
|
||||
|
||||
export interface ToolCall {
|
||||
id: string;
|
||||
type: 'function';
|
||||
function: {
|
||||
name: string;
|
||||
arguments: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChatCompletionRequest {
|
||||
@@ -15,6 +45,8 @@ export interface ChatCompletionRequest {
|
||||
stream?: boolean;
|
||||
temperature?: number;
|
||||
max_tokens?: number;
|
||||
tools?: Tool[];
|
||||
tool_choice?: 'none' | 'auto' | 'required';
|
||||
}
|
||||
|
||||
export interface TextCompletionRequest {
|
||||
@@ -41,7 +73,7 @@ export interface ModelsListResponse {
|
||||
|
||||
export interface ChatCompletionChoice {
|
||||
index: number;
|
||||
message: ChatMessage;
|
||||
message: ChatMessage & { tool_calls?: ToolCall[] };
|
||||
finish_reason: string | null;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ export class DaemonHealthService {
|
||||
async setupHealthListener(): Promise<UnlistenFn | null> {
|
||||
console.log('[DaemonHealth] setupHealthListener() called - starting setup process');
|
||||
try {
|
||||
console.log('[DaemonHealth] About to call listen() for alphahuman:health event');
|
||||
console.log('[DaemonHealth] Setting up alphahuman:health event listener');
|
||||
// console.log('[DaemonHealth] About to call listen() for alphahuman:health event');
|
||||
// console.log('[DaemonHealth] Setting up alphahuman:health event listener');
|
||||
|
||||
this.healthEventListener = await listen<unknown>('alphahuman:health', event => {
|
||||
console.log('[DaemonHealth] Received health event:', event.payload);
|
||||
@@ -44,11 +44,11 @@ export class DaemonHealthService {
|
||||
console.log('[DaemonHealth] alphahuman:health listener created successfully');
|
||||
|
||||
// Start initial timeout
|
||||
console.log('[DaemonHealth] Starting health timeout');
|
||||
// console.log('[DaemonHealth] Starting health timeout');
|
||||
this.startHealthTimeout();
|
||||
console.log('[DaemonHealth] Health timeout started');
|
||||
// console.log('[DaemonHealth] Health timeout started');
|
||||
|
||||
console.log('[DaemonHealth] Health listener setup complete');
|
||||
// console.log('[DaemonHealth] Health listener setup complete');
|
||||
return this.healthEventListener;
|
||||
} catch (error) {
|
||||
console.error('[DaemonHealth] Failed to setup health listener:', error);
|
||||
@@ -147,7 +147,7 @@ export class DaemonHealthService {
|
||||
// Update the health snapshot in Redux
|
||||
store.dispatch(updateHealthSnapshot({ userId, healthSnapshot: snapshot }));
|
||||
|
||||
console.log('[DaemonHealth] Updated health snapshot for user:', userId, snapshot);
|
||||
// console.log('[DaemonHealth] Updated health snapshot for user:', userId, snapshot);
|
||||
} catch (error) {
|
||||
console.error('[DaemonHealth] Error updating Redux from health:', error);
|
||||
}
|
||||
@@ -175,12 +175,7 @@ export class DaemonHealthService {
|
||||
}, this.HEALTH_TIMEOUT_MS);
|
||||
|
||||
// Store timeout ID in Redux for cleanup
|
||||
store.dispatch(
|
||||
setHealthTimeoutId({
|
||||
userId,
|
||||
timeoutId: this.healthTimeoutId.toString(),
|
||||
})
|
||||
);
|
||||
store.dispatch(setHealthTimeoutId({ userId, timeoutId: this.healthTimeoutId.toString() }));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,4 +199,4 @@ export class DaemonHealthService {
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const daemonHealthService = new DaemonHealthService();
|
||||
export const daemonHealthService = new DaemonHealthService();
|
||||
|
||||
Reference in New Issue
Block a user