diff --git a/app/schema.json b/app/schema.json index 93e5bf06a..2bed0647b 100644 --- a/app/schema.json +++ b/app/schema.json @@ -1218,332 +1218,6 @@ } ] }, - { - "description": "Run one-shot agent chat with optional model overrides.", - "function": "agent_chat", - "inputs": [ - { "comment": "User message.", "name": "message", "required": true, "ty": "String" }, - { - "comment": "Optional model override.", - "name": "model_override", - "required": false, - "ty": { "Option": "String" } - }, - { - "comment": "Optional temperature override.", - "name": "temperature", - "required": false, - "ty": { "Option": "F64" } - } - ], - "method": "openhuman.local_ai_agent_chat", - "namespace": "local_ai", - "outputs": [ - { "comment": "Agent response payload.", "name": "response", "required": true, "ty": "Json" } - ] - }, - { - "description": "Run one-shot lightweight provider chat.", - "function": "agent_chat_simple", - "inputs": [ - { "comment": "User message.", "name": "message", "required": true, "ty": "String" }, - { - "comment": "Optional model override.", - "name": "model_override", - "required": false, - "ty": { "Option": "String" } - }, - { - "comment": "Optional temperature override.", - "name": "temperature", - "required": false, - "ty": { "Option": "F64" } - } - ], - "method": "openhuman.local_ai_agent_chat_simple", - "namespace": "local_ai", - "outputs": [ - { "comment": "Agent response payload.", "name": "response", "required": true, "ty": "Json" } - ] - }, - { - "description": "Terminate REPL session.", - "function": "agent_repl_session_end", - "inputs": [ - { "comment": "REPL session id.", "name": "session_id", "required": true, "ty": "String" } - ], - "method": "openhuman.local_ai_agent_repl_session_end", - "namespace": "local_ai", - "outputs": [ - { "comment": "Session end result.", "name": "result", "required": true, "ty": "Json" } - ] - }, - { - "description": "Clear REPL session history.", - "function": "agent_repl_session_reset", - "inputs": [ - { "comment": "REPL session id.", "name": "session_id", "required": true, "ty": "String" } - ], - "method": "openhuman.local_ai_agent_repl_session_reset", - "namespace": "local_ai", - "outputs": [ - { "comment": "Session reset result.", "name": "result", "required": true, "ty": "Json" } - ] - }, - { - "description": "Create a persistent REPL agent session.", - "function": "agent_repl_session_start", - "inputs": [ - { - "comment": "Optional session id.", - "name": "session_id", - "required": false, - "ty": { "Option": "String" } - }, - { - "comment": "Optional model override.", - "name": "model_override", - "required": false, - "ty": { "Option": "String" } - }, - { - "comment": "Optional temperature override.", - "name": "temperature", - "required": false, - "ty": { "Option": "F64" } - } - ], - "method": "openhuman.local_ai_agent_repl_session_start", - "namespace": "local_ai", - "outputs": [ - { "comment": "Session creation result.", "name": "result", "required": true, "ty": "Json" } - ] - }, - { - "description": "Get local AI asset installation status.", - "function": "assets_status", - "inputs": [], - "method": "openhuman.local_ai_assets_status", - "namespace": "local_ai", - "outputs": [ - { "comment": "Assets status payload.", "name": "status", "required": true, "ty": "Json" } - ] - }, - { - "description": "Trigger local AI model download bootstrap.", - "function": "download", - "inputs": [ - { - "comment": "Reset state before download.", - "name": "force", - "required": false, - "ty": { "Option": "Bool" } - } - ], - "method": "openhuman.local_ai_download", - "namespace": "local_ai", - "outputs": [ - { "comment": "Local AI status payload.", "name": "status", "required": true, "ty": "Json" } - ] - }, - { - "description": "Trigger full local AI asset download.", - "function": "download_all_assets", - "inputs": [ - { - "comment": "Reset state before download.", - "name": "force", - "required": false, - "ty": { "Option": "Bool" } - } - ], - "method": "openhuman.local_ai_download_all_assets", - "namespace": "local_ai", - "outputs": [ - { - "comment": "Download progress payload.", - "name": "progress", - "required": true, - "ty": "Json" - } - ] - }, - { - "description": "Trigger download for one local AI asset capability.", - "function": "download_asset", - "inputs": [ - { - "comment": "Asset capability id.", - "name": "capability", - "required": true, - "ty": "String" - } - ], - "method": "openhuman.local_ai_download_asset", - "namespace": "local_ai", - "outputs": [ - { "comment": "Assets status payload.", "name": "status", "required": true, "ty": "Json" } - ] - }, - { - "description": "Get local AI download progress.", - "function": "downloads_progress", - "inputs": [], - "method": "openhuman.local_ai_downloads_progress", - "namespace": "local_ai", - "outputs": [ - { - "comment": "Download progress payload.", - "name": "progress", - "required": true, - "ty": "Json" - } - ] - }, - { - "description": "Generate embeddings for text inputs.", - "function": "embed", - "inputs": [ - { - "comment": "Texts to embed.", - "name": "inputs", - "required": true, - "ty": { "Array": "String" } - } - ], - "method": "openhuman.local_ai_embed", - "namespace": "local_ai", - "outputs": [ - { - "comment": "Embedding result payload.", - "name": "embedding", - "required": true, - "ty": "Json" - } - ] - }, - { - "description": "Run direct local AI prompt.", - "function": "prompt", - "inputs": [ - { "comment": "Prompt text.", "name": "prompt", "required": true, "ty": "String" }, - { - "comment": "Optional max output tokens.", - "name": "max_tokens", - "required": false, - "ty": { "Option": "U64" } - }, - { - "comment": "Disable thinking mode.", - "name": "no_think", - "required": false, - "ty": { "Option": "Bool" } - } - ], - "method": "openhuman.local_ai_prompt", - "namespace": "local_ai", - "outputs": [ - { "comment": "Prompt output text.", "name": "output", "required": true, "ty": "Json" } - ] - }, - { - "description": "Read local AI service status.", - "function": "status", - "inputs": [], - "method": "openhuman.local_ai_status", - "namespace": "local_ai", - "outputs": [ - { "comment": "Local AI status payload.", "name": "status", "required": true, "ty": "Json" } - ] - }, - { - "description": "Summarize text with local AI model.", - "function": "summarize", - "inputs": [ - { "comment": "Input text.", "name": "text", "required": true, "ty": "String" }, - { - "comment": "Optional max output tokens.", - "name": "max_tokens", - "required": false, - "ty": { "Option": "U64" } - } - ], - "method": "openhuman.local_ai_summarize", - "namespace": "local_ai", - "outputs": [{ "comment": "Summary text.", "name": "summary", "required": true, "ty": "Json" }] - }, - { - "description": "Transcribe audio from file path.", - "function": "transcribe", - "inputs": [ - { "comment": "Input audio path.", "name": "audio_path", "required": true, "ty": "String" } - ], - "method": "openhuman.local_ai_transcribe", - "namespace": "local_ai", - "outputs": [ - { "comment": "Transcription payload.", "name": "speech", "required": true, "ty": "Json" } - ] - }, - { - "description": "Transcribe audio from raw bytes.", - "function": "transcribe_bytes", - "inputs": [ - { "comment": "Raw audio bytes.", "name": "audio_bytes", "required": true, "ty": "Bytes" }, - { - "comment": "Optional audio extension.", - "name": "extension", - "required": false, - "ty": { "Option": "String" } - } - ], - "method": "openhuman.local_ai_transcribe_bytes", - "namespace": "local_ai", - "outputs": [ - { "comment": "Transcription payload.", "name": "speech", "required": true, "ty": "Json" } - ] - }, - { - "description": "Synthesize speech from text.", - "function": "tts", - "inputs": [ - { "comment": "Input text.", "name": "text", "required": true, "ty": "String" }, - { - "comment": "Optional output path.", - "name": "output_path", - "required": false, - "ty": { "Option": "String" } - } - ], - "method": "openhuman.local_ai_tts", - "namespace": "local_ai", - "outputs": [ - { "comment": "TTS result payload.", "name": "tts", "required": true, "ty": "Json" } - ] - }, - { - "description": "Run multimodal local AI prompt with image refs.", - "function": "vision_prompt", - "inputs": [ - { "comment": "Prompt text.", "name": "prompt", "required": true, "ty": "String" }, - { - "comment": "Image references to include.", - "name": "image_refs", - "required": true, - "ty": { "Array": "String" } - }, - { - "comment": "Optional max output tokens.", - "name": "max_tokens", - "required": false, - "ty": { "Option": "U64" } - } - ], - "method": "openhuman.local_ai_vision_prompt", - "namespace": "local_ai", - "outputs": [ - { "comment": "Prompt output text.", "name": "output", "required": true, "ty": "Json" } - ] - }, { "description": "Migrate OpenClaw memory into current workspace.", "function": "openclaw", diff --git a/app/src/services/__tests__/rpcMethods.test.ts b/app/src/services/__tests__/rpcMethods.test.ts index 7512ffb14..10ee46b39 100644 --- a/app/src/services/__tests__/rpcMethods.test.ts +++ b/app/src/services/__tests__/rpcMethods.test.ts @@ -131,6 +131,10 @@ describe('rpcMethods catalog', () => { path.resolve(__dirname, '../../../../src/openhuman/inference/schemas.rs'), 'utf8' ), + fs.readFileSync( + path.resolve(__dirname, '../../../../src/openhuman/inference/local/schemas.rs'), + 'utf8' + ), fs.readFileSync( path.resolve(__dirname, '../../../../src/openhuman/embeddings/schemas.rs'), 'utf8' diff --git a/app/src/services/api/__tests__/voiceInstallApi.test.ts b/app/src/services/api/__tests__/voiceInstallApi.test.ts index ce7f51c02..3e3cb4078 100644 --- a/app/src/services/api/__tests__/voiceInstallApi.test.ts +++ b/app/src/services/api/__tests__/voiceInstallApi.test.ts @@ -33,7 +33,7 @@ describe('voiceInstallApi', () => { vi.mocked(callCoreRpc).mockResolvedValueOnce(buildStatus({ engine: 'whisper' })); const result = await installWhisper({ modelSize: 'tiny', force: true }); expect(callCoreRpc).toHaveBeenCalledWith({ - method: 'openhuman.local_ai_install_whisper', + method: 'openhuman.inference_install_whisper', params: { model_size: 'tiny', force: true }, }); expect(result.engine).toBe('whisper'); @@ -45,7 +45,7 @@ describe('voiceInstallApi', () => { vi.mocked(callCoreRpc).mockResolvedValueOnce(buildStatus()); await installWhisper(); expect(callCoreRpc).toHaveBeenCalledWith({ - method: 'openhuman.local_ai_install_whisper', + method: 'openhuman.inference_install_whisper', params: { model_size: undefined, force: undefined }, }); }); @@ -65,7 +65,7 @@ describe('voiceInstallApi', () => { ); const result = await installPiper({ voiceId: 'en_US-lessac-medium', force: false }); expect(callCoreRpc).toHaveBeenCalledWith({ - method: 'openhuman.local_ai_install_piper', + method: 'openhuman.inference_install_piper', params: { voice_id: 'en_US-lessac-medium', force: false }, }); expect(result.state).toBe('installing'); @@ -77,7 +77,7 @@ describe('voiceInstallApi', () => { vi.mocked(callCoreRpc).mockResolvedValueOnce(buildStatus({ engine: 'piper' })); await installPiper(); expect(callCoreRpc).toHaveBeenCalledWith({ - method: 'openhuman.local_ai_install_piper', + method: 'openhuman.inference_install_piper', params: { voice_id: undefined, force: undefined }, }); }); @@ -91,7 +91,7 @@ describe('voiceInstallApi', () => { ); const result = await whisperInstallStatus(); expect(callCoreRpc).toHaveBeenCalledWith({ - method: 'openhuman.local_ai_whisper_install_status', + method: 'openhuman.inference_whisper_install_status', params: {}, }); expect(result.state).toBe('missing'); @@ -106,7 +106,7 @@ describe('voiceInstallApi', () => { ); const result = await piperInstallStatus(); expect(callCoreRpc).toHaveBeenCalledWith({ - method: 'openhuman.local_ai_piper_install_status', + method: 'openhuman.inference_piper_install_status', params: {}, }); expect(result.state).toBe('error'); diff --git a/app/src/services/api/voiceInstallApi.ts b/app/src/services/api/voiceInstallApi.ts index 13c8de4d1..14fadaf40 100644 --- a/app/src/services/api/voiceInstallApi.ts +++ b/app/src/services/api/voiceInstallApi.ts @@ -1,5 +1,5 @@ /** - * Voice engine installer API — wraps the four new `local_ai.*` RPCs that + * Voice engine installer API — wraps the `inference.*` RPCs that * orchestrate downloads of the Whisper GGML model + binary and the Piper * binary + bundled voice into the workspace. * @@ -15,7 +15,7 @@ import { callCoreRpc } from '../coreRpcClient'; const log = debug('voiceInstallApi'); /** - * Stable wire shape of [`crate::openhuman::local_ai::voice_install_common::VoiceInstallState`]. + * Stable wire shape of [`crate::openhuman::inference::local::voice_install_common::VoiceInstallState`]. * * The Rust enum serializes via `#[serde(rename_all = "snake_case")]` so * the TypeScript union mirrors the lowercase variants exactly. @@ -67,7 +67,7 @@ export async function installWhisper( ): Promise { log('[voice-install:whisper] kick-off %o', params); const result = await callCoreRpc({ - method: 'openhuman.local_ai_install_whisper', + method: 'openhuman.inference_install_whisper', params: { model_size: params.modelSize, force: params.force }, }); log('[voice-install:whisper] result state=%s stage=%s', result.state, result.stage ?? ''); @@ -81,7 +81,7 @@ export async function installWhisper( export async function installPiper(params: InstallPiperParams = {}): Promise { log('[voice-install:piper] kick-off %o', params); const result = await callCoreRpc({ - method: 'openhuman.local_ai_install_piper', + method: 'openhuman.inference_install_piper', params: { voice_id: params.voiceId, force: params.force }, }); log('[voice-install:piper] result state=%s stage=%s', result.state, result.stage ?? ''); @@ -96,7 +96,7 @@ export async function installPiper(params: InstallPiperParams = {}): Promise { return await callCoreRpc({ - method: 'openhuman.local_ai_whisper_install_status', + method: 'openhuman.inference_whisper_install_status', params: {}, }); } @@ -107,7 +107,7 @@ export async function whisperInstallStatus(): Promise { */ export async function piperInstallStatus(): Promise { return await callCoreRpc({ - method: 'openhuman.local_ai_piper_install_status', + method: 'openhuman.inference_piper_install_status', params: {}, }); } diff --git a/app/src/services/rpcMethods.ts b/app/src/services/rpcMethods.ts index e7417a9fe..7be9a3d93 100644 --- a/app/src/services/rpcMethods.ts +++ b/app/src/services/rpcMethods.ts @@ -28,14 +28,27 @@ export const CORE_RPC_METHODS = { configWorkspaceOnboardingFlagExists: 'openhuman.config_workspace_onboarding_flag_exists', configWorkspaceOnboardingFlagSet: 'openhuman.config_workspace_onboarding_flag_set', corePing: 'core.ping', + inferenceAgentChat: 'openhuman.inference_agent_chat', + inferenceAgentChatSimple: 'openhuman.inference_agent_chat_simple', inferenceApplyPreset: 'openhuman.inference_apply_preset', + inferenceAssetsStatus: 'openhuman.inference_assets_status', inferenceDiagnostics: 'openhuman.inference_diagnostics', inferenceDeviceProfile: 'openhuman.inference_device_profile', + inferenceDownloadAsset: 'openhuman.inference_download_asset', + inferenceDownloadsProgress: 'openhuman.inference_downloads_progress', inferenceGetClientConfig: 'openhuman.inference_get_client_config', + inferenceInstallPiper: 'openhuman.inference_install_piper', + inferenceInstallWhisper: 'openhuman.inference_install_whisper', inferenceListModels: 'openhuman.inference_list_models', + inferencePiperInstallStatus: 'openhuman.inference_piper_install_status', inferencePresets: 'openhuman.inference_presets', + inferenceTestConnection: 'openhuman.inference_test_connection', + inferenceTranscribe: 'openhuman.inference_transcribe', + inferenceTranscribeBytes: 'openhuman.inference_transcribe_bytes', + inferenceTts: 'openhuman.inference_tts', inferenceUpdateLocalSettings: 'openhuman.inference_update_local_settings', inferenceUpdateModelSettings: 'openhuman.inference_update_model_settings', + inferenceWhisperInstallStatus: 'openhuman.inference_whisper_install_status', providersListModels: 'openhuman.inference_list_models', screenIntelligenceStatus: 'openhuman.screen_intelligence_status', embeddingsGetSettings: 'openhuman.embeddings_get_settings', @@ -81,10 +94,23 @@ export const LEGACY_METHOD_ALIASES: Record = { 'openhuman.workspace_onboarding_flag_exists': CORE_RPC_METHODS.configWorkspaceOnboardingFlagExists, 'openhuman.workspace_onboarding_flag_set': CORE_RPC_METHODS.configWorkspaceOnboardingFlagSet, + 'openhuman.local_ai_agent_chat': CORE_RPC_METHODS.inferenceAgentChat, + 'openhuman.local_ai_agent_chat_simple': CORE_RPC_METHODS.inferenceAgentChatSimple, 'openhuman.local_ai_apply_preset': CORE_RPC_METHODS.inferenceApplyPreset, + 'openhuman.local_ai_assets_status': CORE_RPC_METHODS.inferenceAssetsStatus, 'openhuman.local_ai_device_profile': CORE_RPC_METHODS.inferenceDeviceProfile, 'openhuman.local_ai_diagnostics': CORE_RPC_METHODS.inferenceDiagnostics, + 'openhuman.local_ai_download_asset': CORE_RPC_METHODS.inferenceDownloadAsset, + 'openhuman.local_ai_downloads_progress': CORE_RPC_METHODS.inferenceDownloadsProgress, + 'openhuman.local_ai_install_piper': CORE_RPC_METHODS.inferenceInstallPiper, + 'openhuman.local_ai_install_whisper': CORE_RPC_METHODS.inferenceInstallWhisper, + 'openhuman.local_ai_piper_install_status': CORE_RPC_METHODS.inferencePiperInstallStatus, 'openhuman.local_ai_presets': CORE_RPC_METHODS.inferencePresets, + 'openhuman.local_ai_test_connection': CORE_RPC_METHODS.inferenceTestConnection, + 'openhuman.local_ai_transcribe': CORE_RPC_METHODS.inferenceTranscribe, + 'openhuman.local_ai_transcribe_bytes': CORE_RPC_METHODS.inferenceTranscribeBytes, + 'openhuman.local_ai_tts': CORE_RPC_METHODS.inferenceTts, + 'openhuman.local_ai_whisper_install_status': CORE_RPC_METHODS.inferenceWhisperInstallStatus, 'openhuman.providers_list_models': CORE_RPC_METHODS.inferenceListModels, 'openhuman.inference_embed': CORE_RPC_METHODS.embeddingsEmbed, health_snapshot: CORE_RPC_METHODS.healthSnapshot, diff --git a/app/src/utils/tauriCommands/localAi.test.ts b/app/src/utils/tauriCommands/localAi.test.ts index dccb8816c..fb5d88c8d 100644 --- a/app/src/utils/tauriCommands/localAi.test.ts +++ b/app/src/utils/tauriCommands/localAi.test.ts @@ -7,7 +7,7 @@ describe('openhumanLocalAiTestConnection', () => { vi.clearAllMocks(); }); - it('calls callCoreRpc with local_ai_test_connection method and url param', async () => { + it('calls callCoreRpc with inference_test_connection method and url param', async () => { const { callCoreRpc } = await import('../../services/coreRpcClient'); const mockCallCoreRpc = callCoreRpc as ReturnType; mockCallCoreRpc.mockResolvedValueOnce({ reachable: true, models_count: 4 }); @@ -16,7 +16,7 @@ describe('openhumanLocalAiTestConnection', () => { const result = await openhumanLocalAiTestConnection('http://localhost:11434'); expect(mockCallCoreRpc).toHaveBeenCalledWith({ - method: 'openhuman.local_ai_test_connection', + method: 'openhuman.inference_test_connection', params: { url: 'http://localhost:11434' }, }); expect(result).toEqual({ reachable: true, models_count: 4 }); diff --git a/app/src/utils/tauriCommands/localAi.ts b/app/src/utils/tauriCommands/localAi.ts index c7cccb3e5..e58711b32 100644 --- a/app/src/utils/tauriCommands/localAi.ts +++ b/app/src/utils/tauriCommands/localAi.ts @@ -298,7 +298,7 @@ export async function openhumanLocalAiTranscribe( audioPath: string ): Promise> { return await callCoreRpc>({ - method: 'openhuman.local_ai_transcribe', + method: 'openhuman.inference_transcribe', params: { audio_path: audioPath }, }); } @@ -308,7 +308,7 @@ export async function openhumanLocalAiTranscribeBytes( extension?: string ): Promise> { return await callCoreRpc>({ - method: 'openhuman.local_ai_transcribe_bytes', + method: 'openhuman.inference_transcribe_bytes', params: { audio_bytes: audioBytes, extension }, }); } @@ -318,7 +318,7 @@ export async function openhumanLocalAiTts( outputPath?: string ): Promise> { return await callCoreRpc>({ - method: 'openhuman.local_ai_tts', + method: 'openhuman.inference_tts', params: { text, output_path: outputPath }, }); } @@ -354,7 +354,7 @@ export async function openhumanLocalAiAssetsStatus(): Promise< CommandResponse > { return await callCoreRpc>({ - method: 'openhuman.local_ai_assets_status', + method: 'openhuman.inference_assets_status', }); } @@ -362,7 +362,7 @@ export async function openhumanLocalAiDownloadsProgress(): Promise< CommandResponse > { return await callCoreRpc>({ - method: 'openhuman.local_ai_downloads_progress', + method: 'openhuman.inference_downloads_progress', }); } @@ -370,7 +370,7 @@ export async function openhumanLocalAiDownloadAsset( capability: 'chat' | 'vision' | 'embedding' | 'stt' | 'tts' ): Promise> { return await callCoreRpc>({ - method: 'openhuman.local_ai_download_asset', + method: 'openhuman.inference_download_asset', params: { capability }, }); } @@ -407,7 +407,7 @@ export async function openhumanLocalAiTestConnection( url: string ): Promise { return await callCoreRpc({ - method: 'openhuman.local_ai_test_connection', + method: 'openhuman.inference_test_connection', params: { url }, }); } diff --git a/gitbooks/features/model-routing/local-ai.md b/gitbooks/features/model-routing/local-ai.md index 643a585a9..59c95122b 100644 --- a/gitbooks/features/model-routing/local-ai.md +++ b/gitbooks/features/model-routing/local-ai.md @@ -90,7 +90,7 @@ The `local_ai.usage.*` booleans are consulted only during preset application and Prefer setting the `*_provider` fields directly when editing configuration by hand. -In the desktop app, **Settings → AI & Skills → Local AI** exposes presets, pick one ("embeddings only", "memory + reflection", "everything local") and the right combination of flags is set for you. Status (Ollama reachability, model availability, per-subsystem enablement) is surfaced live via `openhuman.local_ai_status`. +In the desktop app, **Settings → AI & Skills → Local AI** exposes presets, pick one ("embeddings only", "memory + reflection", "everything local") and the right combination of flags is set for you. Status (Ollama reachability, model availability, per-subsystem enablement) is surfaced live via `openhuman.inference_status`. ## When to turn it on @@ -108,7 +108,7 @@ It is **not** worth turning on if you only have a few sources connected, the clo - Enough disk for the models (`gemma3:1b-it-qat` \~700 MB, `all-minilm:latest` \~23 MB). - Enough RAM to keep the model resident (8 GB+ recommended, 16 GB+ ideal). -OpenHuman handles the rest: lifecycle (`src/openhuman/local_ai/service/`), API clients (`ollama_api.rs`, `lm_studio_api.rs`), health checks, and graceful fallback to remote when the local provider disappears. +OpenHuman handles the rest: lifecycle (`src/openhuman/inference/local/service/`), API clients, health checks, and graceful fallback to remote when the local provider disappears. ### LM Studio troubleshooting diff --git a/gitbooks/features/model-routing/local-ai.zh-CN.md b/gitbooks/features/model-routing/local-ai.zh-CN.md index ccce11b31..1c577f8a8 100644 --- a/gitbooks/features/model-routing/local-ai.zh-CN.md +++ b/gitbooks/features/model-routing/local-ai.zh-CN.md @@ -65,7 +65,7 @@ OpenHuman 可以为以下工作负载在你机器上运行本地模型:当本 | `local_ai.usage.learning_reflection` | `false` | 使用本地进行学习通过。 | | `local_ai.usage.subconscious` | `false` | 使用本地进行潜意识循环。 | -在桌面 app 中,**设置 → AI 与技能 → 本地 AI** 暴露预设,选择一个("仅嵌入向量"、"记忆 + 反思"、"全部本地"),正确的 flag 组合会为你设置。状态(Ollama 可达性、模型可用性、每个子系统启用)通过 `openhuman.local_ai_status` 实时暴露。 +在桌面 app 中,**设置 → AI 与技能 → 本地 AI** 暴露预设,选择一个("仅嵌入向量"、"记忆 + 反思"、"全部本地"),正确的 flag 组合会为你设置。状态(Ollama 可达性、模型可用性、每个子系统启用)通过 `openhuman.inference_status` 实时暴露。 ## 何时开启 @@ -83,7 +83,7 @@ OpenHuman 可以为以下工作负载在你机器上运行本地模型:当本 * 模型有足够的磁盘(`gemma3:1b-it-qat` \~700 MB,`all-minilm:latest` \~23 MB)。 * 有足够的 RAM 保持模型驻留(建议 8 GB+,理想 16 GB+)。 -OpenHuman 处理其余:生命周期(`src/openhuman/local_ai/service/`)、API 客户端(`ollama_api.rs`、`lm_studio_api.rs`)、健康检查,以及当本地 provider 消失时优雅地回退到远程。 +OpenHuman 处理其余:生命周期(`src/openhuman/inference/local/service/`)、API 客户端、健康检查,以及当本地 provider 消失时优雅地回退到远程。 ### LM Studio 故障排除 @@ -96,4 +96,4 @@ OpenHuman 处理其余:生命周期(`src/openhuman/local_ai/service/`)、A * [记忆树](../obsidian-wiki/memory-tree.zh-CN.md)。本地嵌入向量 + 摘要 powering 什么。 * [自动模型路由](README.zh-CN.md)。轻量聊天 hint 如何优先使用本地 provider。 -* [隐私与安全](../privacy-and-security.zh-CN.md)。当你 opt-in 时什么移至端侧。 \ No newline at end of file +* [隐私与安全](../privacy-and-security.zh-CN.md)。当你 opt-in 时什么移至端侧。 diff --git a/src/core/all.rs b/src/core/all.rs index 39e7391d0..15621e815 100644 --- a/src/core/all.rs +++ b/src/core/all.rs @@ -179,9 +179,9 @@ fn build_registered_controllers() -> Vec { // Background command monitors for agent-scoped event sources controllers.extend(crate::openhuman::monitor::all_monitor_registered_controllers()); // Unified inference domain: text / vision / local runtime / cloud providers. - // (Formerly split across inference, local_ai, and providers namespaces.) + // (Formerly split across inference, local AI, and providers modules.) controllers.extend(crate::openhuman::inference::all_inference_registered_controllers()); - controllers.extend(crate::openhuman::inference::all_local_ai_registered_controllers()); + controllers.extend(crate::openhuman::inference::all_local_inference_registered_controllers()); // Embedding provider configuration and embed RPC. controllers.extend(crate::openhuman::embeddings::all_embeddings_registered_controllers()); // People resolution and interaction scoring @@ -351,7 +351,7 @@ fn build_declared_controller_schemas() -> Vec { schemas.extend(crate::openhuman::model_council::all_model_council_controller_schemas()); schemas.extend(crate::openhuman::monitor::all_monitor_controller_schemas()); schemas.extend(crate::openhuman::inference::all_inference_controller_schemas()); - schemas.extend(crate::openhuman::inference::all_local_ai_controller_schemas()); + schemas.extend(crate::openhuman::inference::all_local_inference_controller_schemas()); schemas.extend(crate::openhuman::embeddings::all_embeddings_controller_schemas()); schemas.extend(crate::openhuman::people::all_people_controller_schemas()); schemas.extend( @@ -469,7 +469,6 @@ pub fn namespace_description(namespace: &str) -> Option<&'static str> { "encrypt" => Some("Encrypt secure values managed by secret storage."), "health" => Some("Process and component health snapshots."), "inference" => Some("Connect to configured text, vision, and embedding inference runtimes."), - "local_ai" => Some("Local AI chat, inference, downloads, and media operations."), "migrate" => Some("Data migration utilities."), "javascript" => Some("First-class JavaScript runtime bridge for listing and dispatching tools."), "monitor" => Some("Start, inspect, read, and stop bounded background command monitors."), diff --git a/src/core/cli_tests.rs b/src/core/cli_tests.rs index 0b904e707..1dcf86e21 100644 --- a/src/core/cli_tests.rs +++ b/src/core/cli_tests.rs @@ -25,7 +25,6 @@ fn grouped_schemas_contains_migrated_namespaces() { assert!(grouped.contains_key("service")); assert!(grouped.contains_key("migrate")); assert!(grouped.contains_key("inference")); - assert!(grouped.contains_key("local_ai")); } #[test] diff --git a/src/core/legacy_aliases.rs b/src/core/legacy_aliases.rs index a712ee011..c059c9e15 100644 --- a/src/core/legacy_aliases.rs +++ b/src/core/legacy_aliases.rs @@ -111,6 +111,18 @@ const LEGACY_ALIASES: &[(&str, &str)] = &[ "openhuman.local_ai_apply_preset", "openhuman.inference_apply_preset", ), + ( + "openhuman.local_ai_agent_chat", + "openhuman.inference_agent_chat", + ), + ( + "openhuman.local_ai_agent_chat_simple", + "openhuman.inference_agent_chat_simple", + ), + ( + "openhuman.local_ai_assets_status", + "openhuman.inference_assets_status", + ), ( "openhuman.local_ai_device_profile", "openhuman.inference_device_profile", @@ -119,6 +131,26 @@ const LEGACY_ALIASES: &[(&str, &str)] = &[ "openhuman.local_ai_diagnostics", "openhuman.inference_diagnostics", ), + ( + "openhuman.local_ai_download_asset", + "openhuman.inference_download_asset", + ), + ( + "openhuman.local_ai_downloads_progress", + "openhuman.inference_downloads_progress", + ), + ( + "openhuman.local_ai_install_piper", + "openhuman.inference_install_piper", + ), + ( + "openhuman.local_ai_install_whisper", + "openhuman.inference_install_whisper", + ), + ( + "openhuman.local_ai_piper_install_status", + "openhuman.inference_piper_install_status", + ), // bare `health_snapshot` (no namespace prefix) was used by older clients // before the canonical `openhuman.health_snapshot` form was established. ("health_snapshot", "openhuman.health_snapshot"), @@ -128,6 +160,23 @@ const LEGACY_ALIASES: &[(&str, &str)] = &[ ("openhuman.system_info", "openhuman.health_system_info"), ("openhuman.inference_embed", "openhuman.embeddings_embed"), ("openhuman.local_ai_presets", "openhuman.inference_presets"), + ( + "openhuman.local_ai_test_connection", + "openhuman.inference_test_connection", + ), + ( + "openhuman.local_ai_transcribe", + "openhuman.inference_transcribe", + ), + ( + "openhuman.local_ai_transcribe_bytes", + "openhuman.inference_transcribe_bytes", + ), + ("openhuman.local_ai_tts", "openhuman.inference_tts"), + ( + "openhuman.local_ai_whisper_install_status", + "openhuman.inference_whisper_install_status", + ), ( "openhuman.providers_list_models", "openhuman.inference_list_models", diff --git a/src/core/observability.rs b/src/core/observability.rs index c1d13735d..102e74476 100644 --- a/src/core/observability.rs +++ b/src/core/observability.rs @@ -1146,10 +1146,10 @@ fn is_provider_user_state_message(lower: &str) -> bool { /// The two canonical wire shapes today both contain `"for this ram tier"`: /// /// - `"Vision is disabled for this RAM tier. Switch to the 4-8 GB tier or -/// above to enable it."` — from `local_ai/service/assets.rs::ensure_capability_ready` +/// above to enable it."` — from `inference/local/service/assets.rs::ensure_capability_ready` /// - `"vision summaries are unavailable for this RAM tier. Use OCR-only /// summarization or switch to a higher local AI tier."` — -/// from `local_ai/service/vision_embed.rs::summarize` +/// from `inference/local/service/vision_embed.rs::summarize` /// /// Anchor the classifier to that exact substring so an unrelated message /// that merely mentions "RAM tier" out of context is not silenced. diff --git a/src/openhuman/agent/README.md b/src/openhuman/agent/README.md index ceb9c3466..ea386a4b5 100644 --- a/src/openhuman/agent/README.md +++ b/src/openhuman/agent/README.md @@ -22,7 +22,7 @@ Multi-agent orchestration domain. Owns the LLM tool-calling loop, sub-agent disp - `src/openhuman/tools/` — `Tool` / `ToolSpec` execution surface invoked from the tool loop. - `src/openhuman/memory/` — episodic indexing + memory-loader context injection. - `src/openhuman/context/` — prompt sections, tool-call format selection. -- `src/openhuman/local_ai/` — `agent_chat` / `agent_chat_simple` execution backend. +- `src/openhuman/inference/local/` — `agent_chat` / `agent_chat_simple` execution backend. - `src/openhuman/config/` — runtime config load via `config::rpc::load_config_with_timeout`. - `src/core/event_bus/` — emits `DomainEvent::Agent(*)` and `Trigger*` events; subscribers in `agent/bus.rs`. diff --git a/src/openhuman/agent/triage/evaluator.rs b/src/openhuman/agent/triage/evaluator.rs index 774b81342..fe469717b 100644 --- a/src/openhuman/agent/triage/evaluator.rs +++ b/src/openhuman/agent/triage/evaluator.rs @@ -628,7 +628,7 @@ fn classify_error(message: String) -> ArmError { /// `agent.run_turn`). /// - `src/openhuman/agent/harness/session/runtime.rs` — same strings in the /// tool-call loop, kept identical so this classifier covers both. -/// - `src/openhuman/local_ai/ops.rs` — user-facing variants with the +/// - `src/openhuman/inference/local/ops.rs` — user-facing variants with the /// `"Please rephrase clearly."` suffix; we match the leading phrase so /// either form classifies. /// diff --git a/src/openhuman/app_state/README.md b/src/openhuman/app_state/README.md index b64891e83..04d85ae4c 100644 --- a/src/openhuman/app_state/README.md +++ b/src/openhuman/app_state/README.md @@ -17,7 +17,7 @@ Aggregator that the React shell polls every few seconds to render the OS-level c - `src/openhuman/config/` — `config_rpc::*` for `Config` reads and the workspace dir resolver. - `src/openhuman/autocomplete/` — `AutocompleteStatus` snapshot. -- `src/openhuman/local_ai/` — `LocalAiStatus` snapshot. +- `src/openhuman/inference/local/` — `LocalAiStatus` snapshot. - `src/openhuman/screen_intelligence/` — `AccessibilityStatus` snapshot. - `src/openhuman/service/` — `ServiceState` / `ServiceStatus` runtime info. - `src/openhuman/credentials/` — `session_support::build_session_state` for the auth slice. diff --git a/src/openhuman/autocomplete/core/engine.rs b/src/openhuman/autocomplete/core/engine.rs index 93a160a6a..8792e5f88 100644 --- a/src/openhuman/autocomplete/core/engine.rs +++ b/src/openhuman/autocomplete/core/engine.rs @@ -734,7 +734,7 @@ impl AutocompleteEngine { // Interactive variant — bypasses the scheduler_gate's LLM permit // so per-keystroke autocomplete doesn't queue behind a memory-tree // backfill or a triage turn. See `inline_complete_interactive` - // docs in `local_ai/service/public_infer.rs`. + // docs in `inference/local/service/public_infer.rs`. let generated = match service .inline_complete_interactive( &config, diff --git a/src/openhuman/config/README.md b/src/openhuman/config/README.md index 1eb3b6a64..980f5313c 100644 --- a/src/openhuman/config/README.md +++ b/src/openhuman/config/README.md @@ -24,7 +24,7 @@ Authoritative TOML-backed configuration layer. Owns the `Config` schema (every d ## Called by - ~177 sites across the workspace — every domain pulls `Config` for its slice. -- Hot consumers: `src/openhuman/agent/` (model + autonomy), `src/openhuman/channels/` (provider tokens), `src/openhuman/memory/` (storage paths), `src/openhuman/cron/` (scheduler poll), `src/openhuman/local_ai/` (Ollama / device routing), `src/openhuman/security/` (sandbox backend), `src/openhuman/voice/`, `src/openhuman/notifications/`, `src/openhuman/tools/`, `src/openhuman/encryption/`, `src/openhuman/tree_summarizer/`, `src/openhuman/referral/`. +- Hot consumers: `src/openhuman/agent/` (model + autonomy), `src/openhuman/channels/` (provider tokens), `src/openhuman/memory/` (storage paths), `src/openhuman/cron/` (scheduler poll), `src/openhuman/inference/local/` (Ollama / device routing), `src/openhuman/security/` (sandbox backend), `src/openhuman/voice/`, `src/openhuman/notifications/`, `src/openhuman/tools/`, `src/openhuman/encryption/`, `src/openhuman/tree_summarizer/`, `src/openhuman/referral/`. - `src/core/all.rs` — registers `all_config_*`. ## Tests diff --git a/src/openhuman/encryption/README.md b/src/openhuman/encryption/README.md index 3c11dbb90..990fcaa3b 100644 --- a/src/openhuman/encryption/README.md +++ b/src/openhuman/encryption/README.md @@ -24,7 +24,7 @@ AES-256-GCM at-rest crypto for AI memory storage and the encrypt/decrypt RPC sur - `src/openhuman/credentials/` — uses the same `EncryptedPayload` / `EncryptionKey` primitives directly when storing per-channel secrets. - `src/core/all.rs` — registers `all_encryption_*` controllers so the shell + CLI can encrypt configuration secrets. -- Indirect: `src/openhuman/memory/`, `src/openhuman/channels/`, and `src/openhuman/local_ai/` rely on the credentials domain (which in turn uses this layer) for secrets at rest. +- Indirect: `src/openhuman/memory/`, `src/openhuman/channels/`, and `src/openhuman/inference/local/` rely on the credentials domain (which in turn uses this layer) for secrets at rest. ## Tests diff --git a/src/openhuman/inference/README.md b/src/openhuman/inference/README.md index d74e74bf6..3f8cf95e4 100644 --- a/src/openhuman/inference/README.md +++ b/src/openhuman/inference/README.md @@ -1,6 +1,6 @@ # inference -Unified inference domain: the canonical home for everything LLM/STT/TTS/embedding-related. It owns the local-runtime manager (Ollama / LM Studio / Whisper / Piper), the unified cloud + local provider abstraction (trait, factory, router, reliability/retry wrapper), voice transcription and TTS inference, OpenAI/Codex subscription OAuth, and an OpenAI-compatible `/v1/chat/completions` HTTP endpoint. It consolidates the previously separate `local_ai/`, `providers/`, and inference parts of `voice/` under one domain root. The RPC surface is still split across the `inference.*` and `local_ai.*` namespaces for backwards compatibility. +Unified inference domain: the canonical home for everything LLM/STT/TTS/embedding-related. It owns the local-runtime manager (Ollama / LM Studio / Whisper / Piper), the unified cloud + local provider abstraction (trait, factory, router, reliability/retry wrapper), voice transcription and TTS inference, OpenAI/Codex subscription OAuth, and an OpenAI-compatible `/v1/chat/completions` HTTP endpoint. It consolidates the previously separate `local_ai/`, `providers/`, and inference parts of `voice/` under one domain root. The RPC surface is `inference.*`; older `local_ai_*` method names are compatibility aliases in `src/core/legacy_aliases.rs`. ## Responsibilities @@ -33,7 +33,7 @@ Unified inference domain: the canonical home for everything LLM/STT/TTS/embeddin | `local/` | Local runtime manager (was `local_ai/`). | | `local/core.rs` | `LocalAiService` singleton (`global`/`try_global`), `model_artifact_path`. | | `local/ops.rs` | Local RPC entrypoints (`local_ai_status/prompt/summarize/vision_prompt/embed/should_react`, `ReactionDecision`); re-exported as `local::rpc`. | -| `local/schemas.rs` | `local_ai.*` controller schemas + handlers. | +| `local/schemas.rs` | Local-runtime `inference.*` controller schemas + handlers. | | `local/ollama.rs`, `local/lm_studio.rs` | Provider-specific runtime drivers; base-url resolution. | | `local/install*.rs`, `local/voice_install_common.rs` | Whisper/Piper install + shared download logic. | | `local/model_requirements.rs` | `MIN_CONTEXT_TOKENS`, `evaluate_context`, `ContextEligibility`. | @@ -65,18 +65,18 @@ From `mod.rs` re-exports: - `presets::{ModelPreset, ModelTier, VisionMode}` - `sentiment::SentimentResult` - `types::{LocalAiStatus, LocalAiAssetStatus, LocalAiAssetsStatus, LocalAiDownloadProgressItem, LocalAiDownloadsProgress, LocalAiEmbeddingResult, LocalAiSpeechResult, LocalAiTtsResult}` -- `local::all_local_ai_controller_schemas` / `local::all_local_ai_registered_controllers` +- `local::all_local_inference_controller_schemas` / `local::all_local_inference_registered_controllers` (legacy export names; registered schemas are in the `inference` namespace) - `rpc` (alias for `ops`) and `all_inference_controller_schemas` / `all_inference_registered_controllers` Provider-layer (via `provider::`): `Provider`, `ChatMessage`, `ChatRequest`, `ChatResponse`, `create_chat_provider`, `provider_for_role`, `BYOK_INCOMPLETE_SENTINEL`, plus error classifiers. Local runtime: `local::{global, try_global}` → `Arc`. ## RPC / controllers -Two namespaces, both wired into the controller registry (`src/core/all.rs`). +One namespace is wired into the controller registry (`src/core/all.rs`). -`inference.*` (`schemas.rs`): `status`, `get_client_config`, `update_model_settings`, `update_local_settings`, `list_models`, `device_profile`, `presets`, `apply_preset`, `diagnostics`, `openai_oauth_start`, `openai_oauth_complete`, `openai_oauth_status`, `openai_oauth_disconnect`, `summarize`, `prompt`, `vision_prompt`, `test_provider_model`, `should_react`, `analyze_sentiment`. +`inference.*` (`schemas.rs`, `local/schemas.rs`): `status`, `get_client_config`, `update_model_settings`, `update_local_settings`, `list_models`, `device_profile`, `presets`, `apply_preset`, `diagnostics`, `openai_oauth_start`, `openai_oauth_complete`, `openai_oauth_status`, `openai_oauth_disconnect`, `summarize`, `prompt`, `vision_prompt`, `test_provider_model`, `should_react`, `analyze_sentiment`, `agent_chat`, `agent_chat_simple`, `transcribe`, `transcribe_bytes`, `tts`, `assets_status`, `downloads_progress`, `download_asset`, `install_whisper`, `install_piper`, `whisper_install_status`, `piper_install_status`, `test_connection`. -`local_ai.*` (`local/schemas.rs`): `agent_chat`, `agent_chat_simple`, `transcribe`, `transcribe_bytes`, `tts`, `assets_status`, `downloads_progress`, `download_asset`, `install_whisper`, `install_piper`, `whisper_install_status`, `piper_install_status`, `test_connection`. +Legacy `openhuman.local_ai_*` and `openhuman.update_local_ai_settings` method names are rewritten to canonical `openhuman.inference_*` methods by `src/core/legacy_aliases.rs` and `app/src/services/rpcMethods.ts`. Also exposes a non-RPC HTTP router (`http::router()`) nested at `/v1` by `src/core/jsonrpc.rs` (`/v1/chat/completions`, `/v1/models`), accepting either the core bearer or a stable external API key. diff --git a/src/openhuman/inference/local/mod.rs b/src/openhuman/inference/local/mod.rs index 32296a8fd..0891f04d0 100644 --- a/src/openhuman/inference/local/mod.rs +++ b/src/openhuman/inference/local/mod.rs @@ -49,8 +49,8 @@ pub use core::*; pub use ops as rpc; pub use ops::*; pub use schemas::{ - all_controller_schemas as all_local_ai_controller_schemas, - all_registered_controllers as all_local_ai_registered_controllers, + all_controller_schemas as all_local_inference_controller_schemas, + all_registered_controllers as all_local_inference_registered_controllers, }; pub(crate) use service::whisper_engine; pub use service::LocalAiService; diff --git a/src/openhuman/inference/local/schemas.rs b/src/openhuman/inference/local/schemas.rs index cd961c18f..6d3db45ad 100644 --- a/src/openhuman/inference/local/schemas.rs +++ b/src/openhuman/inference/local/schemas.rs @@ -67,17 +67,17 @@ pub fn all_controller_schemas() -> Vec { vec![ schemas("agent_chat"), schemas("agent_chat_simple"), - schemas("local_ai_transcribe"), - schemas("local_ai_transcribe_bytes"), - schemas("local_ai_tts"), - schemas("local_ai_assets_status"), - schemas("local_ai_downloads_progress"), - schemas("local_ai_download_asset"), - schemas("local_ai_install_whisper"), - schemas("local_ai_install_piper"), - schemas("local_ai_whisper_install_status"), - schemas("local_ai_piper_install_status"), - schemas("local_ai_test_connection"), + schemas("transcribe"), + schemas("transcribe_bytes"), + schemas("tts"), + schemas("assets_status"), + schemas("downloads_progress"), + schemas("download_asset"), + schemas("install_whisper"), + schemas("install_piper"), + schemas("whisper_install_status"), + schemas("piper_install_status"), + schemas("test_connection"), ] } @@ -92,47 +92,47 @@ pub fn all_registered_controllers() -> Vec { handler: handle_agent_chat_simple, }, RegisteredController { - schema: schemas("local_ai_transcribe"), + schema: schemas("transcribe"), handler: handle_local_ai_transcribe, }, RegisteredController { - schema: schemas("local_ai_transcribe_bytes"), + schema: schemas("transcribe_bytes"), handler: handle_local_ai_transcribe_bytes, }, RegisteredController { - schema: schemas("local_ai_tts"), + schema: schemas("tts"), handler: handle_local_ai_tts, }, RegisteredController { - schema: schemas("local_ai_assets_status"), + schema: schemas("assets_status"), handler: handle_local_ai_assets_status, }, RegisteredController { - schema: schemas("local_ai_downloads_progress"), + schema: schemas("downloads_progress"), handler: handle_local_ai_downloads_progress, }, RegisteredController { - schema: schemas("local_ai_download_asset"), + schema: schemas("download_asset"), handler: handle_local_ai_download_asset, }, RegisteredController { - schema: schemas("local_ai_install_whisper"), + schema: schemas("install_whisper"), handler: handle_local_ai_install_whisper, }, RegisteredController { - schema: schemas("local_ai_install_piper"), + schema: schemas("install_piper"), handler: handle_local_ai_install_piper, }, RegisteredController { - schema: schemas("local_ai_whisper_install_status"), + schema: schemas("whisper_install_status"), handler: handle_local_ai_whisper_install_status, }, RegisteredController { - schema: schemas("local_ai_piper_install_status"), + schema: schemas("piper_install_status"), handler: handle_local_ai_piper_install_status, }, RegisteredController { - schema: schemas("local_ai_test_connection"), + schema: schemas("test_connection"), handler: handle_local_ai_test_connection, }, ] @@ -141,7 +141,7 @@ pub fn all_registered_controllers() -> Vec { pub fn schemas(function: &str) -> ControllerSchema { match function { "agent_chat" => ControllerSchema { - namespace: "local_ai", + namespace: "inference", function: "agent_chat", description: "Run one-shot agent chat with optional model overrides.", inputs: vec![ @@ -152,7 +152,7 @@ pub fn schemas(function: &str) -> ControllerSchema { outputs: vec![json_output("response", "Agent response payload.")], }, "agent_chat_simple" => ControllerSchema { - namespace: "local_ai", + namespace: "inference", function: "agent_chat_simple", description: "Run one-shot lightweight provider chat.", inputs: vec![ @@ -162,15 +162,15 @@ pub fn schemas(function: &str) -> ControllerSchema { ], outputs: vec![json_output("response", "Agent response payload.")], }, - "local_ai_transcribe" => ControllerSchema { - namespace: "local_ai", + "transcribe" => ControllerSchema { + namespace: "inference", function: "transcribe", description: "Transcribe audio from file path.", inputs: vec![required_string("audio_path", "Input audio path.")], outputs: vec![json_output("speech", "Transcription payload.")], }, - "local_ai_transcribe_bytes" => ControllerSchema { - namespace: "local_ai", + "transcribe_bytes" => ControllerSchema { + namespace: "inference", function: "transcribe_bytes", description: "Transcribe audio from raw bytes.", inputs: vec![ @@ -184,8 +184,8 @@ pub fn schemas(function: &str) -> ControllerSchema { ], outputs: vec![json_output("speech", "Transcription payload.")], }, - "local_ai_tts" => ControllerSchema { - namespace: "local_ai", + "tts" => ControllerSchema { + namespace: "inference", function: "tts", description: "Synthesize speech from text.", inputs: vec![ @@ -194,29 +194,29 @@ pub fn schemas(function: &str) -> ControllerSchema { ], outputs: vec![json_output("tts", "TTS result payload.")], }, - "local_ai_assets_status" => ControllerSchema { - namespace: "local_ai", + "assets_status" => ControllerSchema { + namespace: "inference", function: "assets_status", description: "Get local AI asset installation status.", inputs: vec![], outputs: vec![json_output("status", "Assets status payload.")], }, - "local_ai_downloads_progress" => ControllerSchema { - namespace: "local_ai", + "downloads_progress" => ControllerSchema { + namespace: "inference", function: "downloads_progress", description: "Get local AI download progress.", inputs: vec![], outputs: vec![json_output("progress", "Download progress payload.")], }, - "local_ai_download_asset" => ControllerSchema { - namespace: "local_ai", + "download_asset" => ControllerSchema { + namespace: "inference", function: "download_asset", description: "Trigger download for one local AI asset capability.", inputs: vec![required_string("capability", "Asset capability id.")], outputs: vec![json_output("status", "Assets status payload.")], }, - "local_ai_install_whisper" => ControllerSchema { - namespace: "local_ai", + "install_whisper" => ControllerSchema { + namespace: "inference", function: "install_whisper", description: "Download whisper.cpp's GGML model (and on Windows the whisper-cli binary) into the workspace so the local STT factory has everything it needs to run.", inputs: vec![ @@ -231,8 +231,8 @@ pub fn schemas(function: &str) -> ControllerSchema { ], outputs: vec![json_output("status", "Whisper install status payload.")], }, - "local_ai_install_piper" => ControllerSchema { - namespace: "local_ai", + "install_piper" => ControllerSchema { + namespace: "inference", function: "install_piper", description: "Download the Piper binary archive and the bundled en_US-lessac-medium voice files into the workspace.", inputs: vec![ @@ -247,31 +247,31 @@ pub fn schemas(function: &str) -> ControllerSchema { ], outputs: vec![json_output("status", "Piper install status payload.")], }, - "local_ai_whisper_install_status" => ControllerSchema { - namespace: "local_ai", + "whisper_install_status" => ControllerSchema { + namespace: "inference", function: "whisper_install_status", description: "Query the Whisper install state (missing / installing / installed / broken / error) plus per-stage download progress.", inputs: vec![], outputs: vec![json_output("status", "Whisper install status payload.")], }, - "local_ai_piper_install_status" => ControllerSchema { - namespace: "local_ai", + "piper_install_status" => ControllerSchema { + namespace: "inference", function: "piper_install_status", description: "Query the Piper install state (missing / installing / installed / broken / error) plus per-stage download progress.", inputs: vec![], outputs: vec![json_output("status", "Piper install status payload.")], }, - "local_ai_test_connection" => ControllerSchema { - namespace: "local_ai", + "test_connection" => ControllerSchema { + namespace: "inference", function: "test_connection", description: "Test connectivity to an Ollama server URL. Returns reachable status and model count.", inputs: vec![required_string("url", "Ollama server URL to test.")], outputs: vec![json_output("result", "Connection test result.")], }, _ => ControllerSchema { - namespace: "local_ai", + namespace: "inference", function: "unknown", - description: "Unknown local_ai controller function.", + description: "Unknown local inference controller function.", inputs: vec![], outputs: vec![FieldSchema { name: "error", diff --git a/src/openhuman/inference/local/schemas_tests.rs b/src/openhuman/inference/local/schemas_tests.rs index 8f7a6f329..94cefe6a1 100644 --- a/src/openhuman/inference/local/schemas_tests.rs +++ b/src/openhuman/inference/local/schemas_tests.rs @@ -5,13 +5,16 @@ fn catalog_counts_match_and_nonempty() { let s = all_controller_schemas(); let h = all_registered_controllers(); assert_eq!(s.len(), h.len()); - assert!(s.len() >= 12, "local_ai should expose >=12 controller fns"); + assert!( + s.len() >= 12, + "local inference should expose >=12 controller fns" + ); } #[test] -fn all_schemas_use_local_ai_namespace_and_have_descriptions() { +fn all_schemas_use_inference_namespace_and_have_descriptions() { for s in all_controller_schemas() { - assert_eq!(s.namespace, "local_ai", "function {}", s.function); + assert_eq!(s.namespace, "inference", "function {}", s.function); assert!(!s.description.is_empty(), "function {} desc", s.function); assert!(!s.outputs.is_empty(), "function {} outputs", s.function); } @@ -21,7 +24,7 @@ fn all_schemas_use_local_ai_namespace_and_have_descriptions() { fn unknown_function_returns_unknown_schema() { let s = schemas("no_such_fn"); assert_eq!(s.function, "unknown"); - assert_eq!(s.namespace, "local_ai"); + assert_eq!(s.namespace, "inference"); } #[test] @@ -29,29 +32,29 @@ fn every_registered_key_resolves_to_non_unknown_schema() { let keys = [ "agent_chat", "agent_chat_simple", - "local_ai_transcribe", - "local_ai_transcribe_bytes", - "local_ai_tts", - "local_ai_assets_status", - "local_ai_downloads_progress", - "local_ai_download_asset", - "local_ai_install_whisper", - "local_ai_install_piper", - "local_ai_whisper_install_status", - "local_ai_piper_install_status", - "local_ai_test_connection", + "transcribe", + "transcribe_bytes", + "tts", + "assets_status", + "downloads_progress", + "download_asset", + "install_whisper", + "install_piper", + "whisper_install_status", + "piper_install_status", + "test_connection", ]; for k in keys { let s = schemas(k); - assert_eq!(s.namespace, "local_ai"); + assert_eq!(s.namespace, "inference"); assert_ne!(s.function, "unknown", "key `{k}` fell through"); } } #[test] -fn registered_controllers_all_in_local_ai_namespace() { +fn registered_controllers_all_in_inference_namespace() { for h in all_registered_controllers() { - assert_eq!(h.schema.namespace, "local_ai"); + assert_eq!(h.schema.namespace, "inference"); assert!(!h.schema.function.is_empty()); } } diff --git a/src/openhuman/inference/mod.rs b/src/openhuman/inference/mod.rs index 7a04d75ec..b0c4bfb29 100644 --- a/src/openhuman/inference/mod.rs +++ b/src/openhuman/inference/mod.rs @@ -9,8 +9,8 @@ //! (moved from `src/openhuman/voice/`) //! - `http/` — OpenAI-compatible `/v1/chat/completions` endpoint //! -//! The RPC surface remains under the `inference.*` and `local_ai.*` namespaces -//! for backwards compatibility. +//! The RPC surface is `inference.*`; old `local_ai_*` RPC names are resolved +//! by the legacy alias layer for backwards compatibility. pub mod device; pub mod http; @@ -36,8 +36,8 @@ pub use schemas::{ // Re-export the types that external callers (voice, agent, etc.) import from inference pub use device::DeviceProfile; -pub use local::all_local_ai_controller_schemas; -pub use local::all_local_ai_registered_controllers; +pub use local::all_local_inference_controller_schemas; +pub use local::all_local_inference_registered_controllers; pub use model_context::context_window_for_model; pub use presets::{ModelPreset, ModelTier, VisionMode}; pub use sentiment::SentimentResult; diff --git a/src/openhuman/inference/voice/local_speech.rs b/src/openhuman/inference/voice/local_speech.rs index 3c966daac..ee41f2146 100644 --- a/src/openhuman/inference/voice/local_speech.rs +++ b/src/openhuman/inference/voice/local_speech.rs @@ -39,7 +39,7 @@ //! into `~/.openhuman/bin/piper/`, extracts it, and stages the bundled //! `en_US-lessac-medium` voice (`.onnx` + `.onnx.json`) alongside via a //! `.part` file + atomic rename. After install the `resolve_piper_binary` -//! helper in `local_ai/paths.rs` picks it up automatically. +//! helper in `inference/paths.rs` picks it up automatically. //! //! **Advanced path:** download Piper from //! [rhasspy/piper](https://github.com/rhasspy/piper) releases (one diff --git a/src/openhuman/inference/voice/local_transcribe.rs b/src/openhuman/inference/voice/local_transcribe.rs index 7822b4f74..ecf6899dc 100644 --- a/src/openhuman/inference/voice/local_transcribe.rs +++ b/src/openhuman/inference/voice/local_transcribe.rs @@ -20,7 +20,7 @@ //! `~/.openhuman/bin/whisper/` via a `.part` file + atomic rename, plus //! the `whisper-cli` binary on Windows where upstream ships a release //! archive. After install the `resolve_whisper_binary` helper in -//! `local_ai/paths.rs` picks it up automatically — no env var to set. +//! `inference/paths.rs` picks it up automatically — no env var to set. //! //! **Advanced path:** install whisper.cpp's `whisper-cli` from a package //! manager (`brew install whisper-cpp`, `pacman -S whisper.cpp`, …) or diff --git a/tests/inference_agent_raw_coverage_e2e.rs b/tests/inference_agent_raw_coverage_e2e.rs index eb9f0121f..48083e495 100644 --- a/tests/inference_agent_raw_coverage_e2e.rs +++ b/tests/inference_agent_raw_coverage_e2e.rs @@ -172,7 +172,8 @@ use openhuman_core::openhuman::inference::voice::local_speech::{synthesize_piper use openhuman_core::openhuman::inference::voice::postprocess::cleanup_transcription; use openhuman_core::openhuman::inference::{ all_inference_controller_schemas, all_inference_registered_controllers, - all_local_ai_controller_schemas, all_local_ai_registered_controllers, DeviceProfile, + all_local_inference_controller_schemas, all_local_inference_registered_controllers, + DeviceProfile, }; use openhuman_core::openhuman::memory::{Memory, MemoryCategory, MemoryEntry, RecallOpts}; use openhuman_core::openhuman::security::SecurityPolicy; @@ -3018,13 +3019,13 @@ async fn inference_local_controllers_and_presets_cover_public_paths() { let _ollama_bin_guard = EnvVarGuard::set("OLLAMA_BIN", &mock_ollama); let _ollama_base_guard = EnvVarGuard::set("OPENHUMAN_OLLAMA_BASE_URL", &provider_base); - let local_schemas = all_local_ai_controller_schemas(); - let local_registered = all_local_ai_registered_controllers(); + let local_schemas = all_local_inference_controller_schemas(); + let local_registered = all_local_inference_registered_controllers(); assert_eq!(local_schemas.len(), local_registered.len()); assert!(local_registered.iter().all(|controller| { controller .rpc_method_name() - .starts_with("openhuman.local_ai_") + .starts_with("openhuman.inference_") })); let reachable = call( diff --git a/tests/inference_compatible_admin_round25_raw_coverage_e2e.rs b/tests/inference_compatible_admin_round25_raw_coverage_e2e.rs index 062794001..93cdc7907 100644 --- a/tests/inference_compatible_admin_round25_raw_coverage_e2e.rs +++ b/tests/inference_compatible_admin_round25_raw_coverage_e2e.rs @@ -20,7 +20,7 @@ use openhuman_core::openhuman::config::schema::cloud_providers::{ }; use openhuman_core::openhuman::config::Config; use openhuman_core::openhuman::credentials::{AuthService, DEFAULT_AUTH_PROFILE_NAME}; -use openhuman_core::openhuman::inference::local::all_local_ai_registered_controllers; +use openhuman_core::openhuman::inference::local::all_local_inference_registered_controllers; use openhuman_core::openhuman::inference::ops::inference_test_provider_model; use openhuman_core::openhuman::inference::provider::compatible::{ AuthStyle as CompatibleAuthStyle, OpenAiCompatibleProvider, @@ -226,7 +226,7 @@ async fn provider_admin_cold_paths_cover_model_errors_local_factory_and_connecti .expect_err("empty lmstudio model"); assert!(empty_lmstudio.contains("empty model")); - let controllers = all_local_ai_registered_controllers(); + let controllers = all_local_inference_registered_controllers(); let test_connection = controller(&controllers, "test_connection"); let reachable = call(test_connection, json!({"url": base})) .await diff --git a/tests/inference_local_ops_piper_raw_coverage_e2e.rs b/tests/inference_local_ops_piper_raw_coverage_e2e.rs index fec77897f..ebc356ca0 100644 --- a/tests/inference_local_ops_piper_raw_coverage_e2e.rs +++ b/tests/inference_local_ops_piper_raw_coverage_e2e.rs @@ -18,7 +18,7 @@ use flate2::Compression; use openhuman_core::core::all::RegisteredController; use openhuman_core::openhuman::config::Config; use openhuman_core::openhuman::inference::local::{ - all_local_ai_registered_controllers, local_ai_transcribe_bytes, + all_local_inference_registered_controllers, local_ai_transcribe_bytes, }; use serde_json::{json, Value}; use tempfile::{tempdir, TempDir}; @@ -107,7 +107,7 @@ async fn piper_controller_installs_skips_existing_and_records_failures_from_mock let _piper_bin = EnvVarGuard::unset("PIPER_BIN"); let _whisper_bin = EnvVarGuard::unset("WHISPER_BIN"); - let controllers = all_local_ai_registered_controllers(); + let controllers = all_local_inference_registered_controllers(); let install = controller(&controllers, "install_piper"); let status = controller(&controllers, "piper_install_status"); diff --git a/tests/inference_local_services_round21_raw_coverage_e2e.rs b/tests/inference_local_services_round21_raw_coverage_e2e.rs index b451f622c..918044ac4 100644 --- a/tests/inference_local_services_round21_raw_coverage_e2e.rs +++ b/tests/inference_local_services_round21_raw_coverage_e2e.rs @@ -21,7 +21,7 @@ use openhuman_core::openhuman::inference::local::ops::{ LocalAiChatMessage, }; use openhuman_core::openhuman::inference::local::{ - all_local_ai_registered_controllers, LocalAiService, + all_local_inference_registered_controllers, LocalAiService, }; use serde_json::{json, Value}; use tempfile::{tempdir, TempDir}; @@ -295,7 +295,7 @@ async fn local_services_cover_mocked_inference_assets_speech_and_whisper_install "ready" ); - let controllers = all_local_ai_registered_controllers(); + let controllers = all_local_inference_registered_controllers(); let install = controller(&controllers, "install_whisper"); let status = controller(&controllers, "whisper_install_status"); diff --git a/tests/json_rpc_e2e.rs b/tests/json_rpc_e2e.rs index b463c4fbd..22efd5b2a 100644 --- a/tests/json_rpc_e2e.rs +++ b/tests/json_rpc_e2e.rs @@ -1764,7 +1764,7 @@ async fn json_rpc_protocol_auth_and_agent_hello_inner() { let chat = post_json_rpc( &rpc_base, 5, - "openhuman.local_ai_agent_chat", + "openhuman.inference_agent_chat", json!({ "message": "Hello", }), @@ -2207,14 +2207,14 @@ async fn json_rpc_prompt_injection_is_rejected_before_model_call() { let blocked_agent = post_json_rpc( &rpc_base, 4003, - "openhuman.local_ai_agent_chat", + "openhuman.inference_agent_chat", json!({ "message": payload, "model_override": "e2e-mock-model", }), ) .await; - let agent_err = assert_jsonrpc_error(&blocked_agent, "local_ai_agent_chat blocked"); + let agent_err = assert_jsonrpc_error(&blocked_agent, "inference_agent_chat blocked"); let agent_msg = agent_err .get("message") .and_then(Value::as_str)