mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
refactor(inference): retire local_ai rpc namespace (#3476)
This commit is contained in:
-326
@@ -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",
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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<VoiceInstallStatus> {
|
||||
log('[voice-install:whisper] kick-off %o', params);
|
||||
const result = await callCoreRpc<VoiceInstallStatus>({
|
||||
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 ?? '<none>');
|
||||
@@ -81,7 +81,7 @@ export async function installWhisper(
|
||||
export async function installPiper(params: InstallPiperParams = {}): Promise<VoiceInstallStatus> {
|
||||
log('[voice-install:piper] kick-off %o', params);
|
||||
const result = await callCoreRpc<VoiceInstallStatus>({
|
||||
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 ?? '<none>');
|
||||
@@ -96,7 +96,7 @@ export async function installPiper(params: InstallPiperParams = {}): Promise<Voi
|
||||
*/
|
||||
export async function whisperInstallStatus(): Promise<VoiceInstallStatus> {
|
||||
return await callCoreRpc<VoiceInstallStatus>({
|
||||
method: 'openhuman.local_ai_whisper_install_status',
|
||||
method: 'openhuman.inference_whisper_install_status',
|
||||
params: {},
|
||||
});
|
||||
}
|
||||
@@ -107,7 +107,7 @@ export async function whisperInstallStatus(): Promise<VoiceInstallStatus> {
|
||||
*/
|
||||
export async function piperInstallStatus(): Promise<VoiceInstallStatus> {
|
||||
return await callCoreRpc<VoiceInstallStatus>({
|
||||
method: 'openhuman.local_ai_piper_install_status',
|
||||
method: 'openhuman.inference_piper_install_status',
|
||||
params: {},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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<string, CoreRpcMethod> = {
|
||||
'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,
|
||||
|
||||
@@ -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<typeof vi.fn>;
|
||||
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 });
|
||||
|
||||
@@ -298,7 +298,7 @@ export async function openhumanLocalAiTranscribe(
|
||||
audioPath: string
|
||||
): Promise<CommandResponse<LocalAiSpeechResult>> {
|
||||
return await callCoreRpc<CommandResponse<LocalAiSpeechResult>>({
|
||||
method: 'openhuman.local_ai_transcribe',
|
||||
method: 'openhuman.inference_transcribe',
|
||||
params: { audio_path: audioPath },
|
||||
});
|
||||
}
|
||||
@@ -308,7 +308,7 @@ export async function openhumanLocalAiTranscribeBytes(
|
||||
extension?: string
|
||||
): Promise<CommandResponse<LocalAiSpeechResult>> {
|
||||
return await callCoreRpc<CommandResponse<LocalAiSpeechResult>>({
|
||||
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<CommandResponse<LocalAiTtsResult>> {
|
||||
return await callCoreRpc<CommandResponse<LocalAiTtsResult>>({
|
||||
method: 'openhuman.local_ai_tts',
|
||||
method: 'openhuman.inference_tts',
|
||||
params: { text, output_path: outputPath },
|
||||
});
|
||||
}
|
||||
@@ -354,7 +354,7 @@ export async function openhumanLocalAiAssetsStatus(): Promise<
|
||||
CommandResponse<LocalAiAssetsStatus>
|
||||
> {
|
||||
return await callCoreRpc<CommandResponse<LocalAiAssetsStatus>>({
|
||||
method: 'openhuman.local_ai_assets_status',
|
||||
method: 'openhuman.inference_assets_status',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -362,7 +362,7 @@ export async function openhumanLocalAiDownloadsProgress(): Promise<
|
||||
CommandResponse<LocalAiDownloadsProgress>
|
||||
> {
|
||||
return await callCoreRpc<CommandResponse<LocalAiDownloadsProgress>>({
|
||||
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<CommandResponse<LocalAiAssetsStatus>> {
|
||||
return await callCoreRpc<CommandResponse<LocalAiAssetsStatus>>({
|
||||
method: 'openhuman.local_ai_download_asset',
|
||||
method: 'openhuman.inference_download_asset',
|
||||
params: { capability },
|
||||
});
|
||||
}
|
||||
@@ -407,7 +407,7 @@ export async function openhumanLocalAiTestConnection(
|
||||
url: string
|
||||
): Promise<OllamaConnectionTestResult> {
|
||||
return await callCoreRpc<OllamaConnectionTestResult>({
|
||||
method: 'openhuman.local_ai_test_connection',
|
||||
method: 'openhuman.inference_test_connection',
|
||||
params: { url },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 时什么移至端侧。
|
||||
* [隐私与安全](../privacy-and-security.zh-CN.md)。当你 opt-in 时什么移至端侧。
|
||||
|
||||
+3
-4
@@ -179,9 +179,9 @@ fn build_registered_controllers() -> Vec<RegisteredController> {
|
||||
// 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<ControllerSchema> {
|
||||
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."),
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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<LocalAiService>`.
|
||||
|
||||
## 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.
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -67,17 +67,17 @@ pub fn all_controller_schemas() -> Vec<ControllerSchema> {
|
||||
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<RegisteredController> {
|
||||
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<RegisteredController> {
|
||||
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",
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user