- Added GenAI API User Guide.md with complete API specification - Added HANDOFF-MSI-GENAI.md documenting custom provider implementation - Includes API endpoints, request/response formats, available models, and rate limits
10 KiB
MSI GenAI Custom Provider Integration - Handoff Document
Date: 2026-04-03 Status: In Progress - Backend schema updated, frontend and provider logic pending
Context
User needs to integrate MSI GenAI API (https://genai-service.stage.commandcentral.com/app-gateway/api/v2/chat) into the application's AI Providers system.
Problem: The existing "Custom" provider type assumes OpenAI-compatible APIs (expects /chat/completions endpoint, OpenAI request/response format, Authorization: Bearer header). MSI GenAI has a completely different API contract:
| Aspect | OpenAI Format | MSI GenAI Format |
|---|---|---|
| Endpoint | /chat/completions |
/api/v2/chat (no suffix) |
| Request | {"messages": [...], "model": "..."} |
{"prompt": "...", "model": "...", "sessionId": "..."} |
| Response | {"choices": [{"message": {"content": "..."}}]} |
{"msg": "...", "sessionId": "..."} |
| Auth Header | Authorization: Bearer <token> |
x-msi-genai-api-key: <token> |
| History | Client sends full message array | Server-side via sessionId |
Work Completed
1. Updated src-tauri/src/state.rs - ProviderConfig Schema
Added optional fields to support custom API formats without breaking existing OpenAI-compatible providers:
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProviderConfig {
pub name: String,
#[serde(default)]
pub provider_type: String,
pub api_url: String,
pub api_key: String,
pub model: String,
// NEW FIELDS:
/// Optional: Custom endpoint path (e.g., "" for no path, "/v1/chat" for custom path)
/// If None, defaults to "/chat/completions" for OpenAI compatibility
#[serde(skip_serializing_if = "Option::is_none")]
pub custom_endpoint_path: Option<String>,
/// Optional: Custom auth header name (e.g., "x-msi-genai-api-key")
/// If None, defaults to "Authorization"
#[serde(skip_serializing_if = "Option::is_none")]
pub custom_auth_header: Option<String>,
/// Optional: Custom auth value prefix (e.g., "" for no prefix, "Bearer " for OpenAI)
/// If None, defaults to "Bearer "
#[serde(skip_serializing_if = "Option::is_none")]
pub custom_auth_prefix: Option<String>,
/// Optional: API format ("openai" or "msi_genai")
/// If None, defaults to "openai"
#[serde(skip_serializing_if = "Option::is_none")]
pub api_format: Option<String>,
/// Optional: Session ID for stateful APIs like MSI GenAI
#[serde(skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
}
Design philosophy: Existing providers remain unchanged (all fields default to OpenAI-compatible behavior). Only when api_format is set to "msi_genai" do the custom fields take effect.
Work Remaining
2. Update src-tauri/src/ai/openai.rs - Support Custom Formats
The OpenAiProvider::chat() method needs to conditionally handle MSI GenAI format:
Changes needed:
- Check
config.api_format— ifSome("msi_genai"), use MSI GenAI request/response logic - Use
config.custom_endpoint_path.unwrap_or("/chat/completions")for endpoint - Use
config.custom_auth_header.unwrap_or("Authorization")for header name - Use
config.custom_auth_prefix.unwrap_or("Bearer ")for auth prefix
MSI GenAI request format:
{
"model": "VertexGemini",
"prompt": "<last user message>",
"system": "<optional system message>",
"sessionId": "<uuid or null for first message>",
"userId": "user@motorolasolutions.com"
}
MSI GenAI response format:
{
"status": true,
"sessionId": "uuid",
"msg": "AI response text",
"initialPrompt": true/false
}
Implementation notes:
- For MSI GenAI, convert
Vec<Message>to a singleprompt(concatenate or use last user message) - Extract system message from messages array if present (role == "system")
- Store returned
sessionIdback toconfig.session_idfor subsequent requests - Extract response content from
json["msg"]instead ofjson["choices"][0]["message"]["content"]
3. Update src/lib/tauriCommands.ts - TypeScript Types
Add new optional fields to ProviderConfig interface:
export interface ProviderConfig {
provider_type?: string;
max_tokens?: number;
temperature?: number;
name: string;
api_url: string;
api_key: string;
model: string;
// NEW FIELDS:
custom_endpoint_path?: string;
custom_auth_header?: string;
custom_auth_prefix?: string;
api_format?: string;
session_id?: string;
}
4. Update src/pages/Settings/AIProviders.tsx - UI Fields
When provider_type === "custom", show additional form fields:
{form.provider_type === "custom" && (
<>
<div className="space-y-2">
<Label>API Format</Label>
<Select
value={form.api_format ?? "openai"}
onValueChange={(v) => {
const format = v;
const defaults = format === "msi_genai"
? {
custom_endpoint_path: "",
custom_auth_header: "x-msi-genai-api-key",
custom_auth_prefix: "",
}
: {
custom_endpoint_path: "/chat/completions",
custom_auth_header: "Authorization",
custom_auth_prefix: "Bearer ",
};
setForm({ ...form, api_format: format, ...defaults });
}}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="openai">OpenAI Compatible</SelectItem>
<SelectItem value="msi_genai">MSI GenAI</SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label>Endpoint Path</Label>
<Input
value={form.custom_endpoint_path ?? ""}
onChange={(e) => setForm({ ...form, custom_endpoint_path: e.target.value })}
placeholder="/chat/completions"
/>
</div>
<div className="space-y-2">
<Label>Auth Header Name</Label>
<Input
value={form.custom_auth_header ?? ""}
onChange={(e) => setForm({ ...form, custom_auth_header: e.target.value })}
placeholder="Authorization"
/>
</div>
</div>
<div className="space-y-2">
<Label>Auth Prefix</Label>
<Input
value={form.custom_auth_prefix ?? ""}
onChange={(e) => setForm({ ...form, custom_auth_prefix: e.target.value })}
placeholder="Bearer "
/>
<p className="text-xs text-muted-foreground">
Prefix added before API key (e.g., "Bearer " for OpenAI, "" for MSI GenAI)
</p>
</div>
</>
)}
Update emptyProvider initial state:
const emptyProvider: ProviderConfig = {
name: "",
provider_type: "openai",
api_url: "",
api_key: "",
model: "",
custom_endpoint_path: undefined,
custom_auth_header: undefined,
custom_auth_prefix: undefined,
api_format: undefined,
session_id: undefined,
};
Testing Configuration
For MSI GenAI:
- Type: Custom
- API Format: MSI GenAI
- API URL:
https://genai-service.stage.commandcentral.com/app-gateway - Model:
VertexGemini(orClaude-Sonnet-4,ChatGPT4o) - API Key: (user's MSI GenAI API key from portal)
- Endpoint Path: `` (empty - URL already includes
/api/v2/chat) - Auth Header:
x-msi-genai-api-key - Auth Prefix: `` (empty - no "Bearer " prefix)
Test command flow:
- Create provider with above settings
- Test connection (should receive AI response)
- Verify
sessionIdis returned and stored - Send second message (should reuse
sessionIdfor conversation history)
Known Issues from User's Original Error
User initially tried:
- API URL:
https://genai-service.stage.commandcentral.com/app-gateway/api/v2/chat - Type: Custom (no format specified)
Result: Cannot POST /api/v2/chat/chat/completions (404)
Root cause: OpenAI provider appends /chat/completions to base URL. With the new custom_endpoint_path field, this is now configurable.
Integration with Existing Session Management
MSI GenAI uses server-side session management. Current triage flow sends full message history on every request (OpenAI style). For MSI GenAI:
- First message: Send
sessionId: nullor omit field - Store response: Save
response.sessionIdtoconfig.session_id - Subsequent messages: Include
sessionIdin requests (server maintains history)
Consider storing session_id per conversation in the database (link to ai_conversations.id) rather than globally in ProviderConfig.
Commit Strategy
Current git state:
- Modified by other session:
src-tauri/src/integrations/*.rs(ADO/Confluence/ServiceNow work) - Modified by me:
src-tauri/src/state.rs(MSI GenAI schema) - Untracked:
GenAI API User Guide.md
Recommended approach:
- Other session commits first: Commit integration changes to main
- Then complete MSI GenAI work: Finish items 2-4 above, test, commit separately
Alternative: Create feature branch feature/msi-genai-custom-provider, cherry-pick only MSI GenAI changes, complete work there, merge when ready.
Reference: MSI GenAI API Spec
Documentation: GenAI API User Guide.md (in project root)
Key endpoints:
POST /api/v2/chat- Send prompt, get responsePOST /api/v2/upload/<SESSION-ID>- Upload files (requires session)GET /api/v2/getSessionMessages/<SESSION-ID>- Retrieve historyDELETE /api/v2/entry/<MSG-ID>- Delete message
Available models (from guide):
Claude-Sonnet-4(Public)ChatGPT4o(Public)VertexGemini(Private) - Gemini 2.0 FlashChatGPT-5_2-Chat(Public)- Many others (see guide section 4.1)
Rate limits: $50/user/month (enforced server-side)
Questions for User
- Should
session_idbe stored globally inProviderConfigor per-conversation in DB? - Do we need to support file uploads via
/api/v2/upload/<SESSION-ID>? - Should we expose model config options (temperature, max_tokens) for MSI GenAI?
Contact
This handoff doc was generated for the other Claude Code session working on integration files. Once that work is committed, this MSI GenAI work can be completed as a separate commit or feature branch.