How OpenCTI Integrates with AI/ML Models Such as Mistral AI: A Technical Deep Dive
OpenCTI integrates with Mistral AI through a unified AI layer that reads runtime configuration, instantiates provider-specific clients, and exposes streaming capabilities via GraphQL mutations for features like natural language querying and text summarization.
The OpenCTI-Platform/opencti repository implements a provider-agnostic AI architecture that supports Mistral AI, OpenAI, and Azure OpenAI through a consistent abstraction layer. This OpenCTI AI/ML integration enables cybersecurity teams to leverage large language models for automated threat intelligence processing without modifying core business logic.
Configuration-Driven AI Provider Setup
OpenCTI uses the nconf configuration system to manage AI runtime settings. The platform reads AI parameters from config.yml or environment variables to determine which provider to instantiate.
Key configuration parameters include:
ai:enabled– Boolean flag to activate the AI layerai:type– Provider identifier (mistralai,openai, orazureopenai)ai:endpoint– API base URL (e.g.,https://api.mistral.ai)ai:token– Authentication API keyai:model– Model name (e.g.,mistral-large)
In opencti-platform/opencti-graphql/src/database/ai-llm.ts (lines 18–27), the platform retrieves these values:
const AI_ENABLED = conf.get('ai:enabled');
const AI_TYPE = conf.get('ai:type');
const AI_ENDPOINT = conf.get('ai:endpoint');
const AI_TOKEN = conf.get('ai:token');
const AI_MODEL = conf.get('ai:model');
Client Initialization and Provider Selection
The integration supports both official Mistral endpoints and OpenAI-compatible interfaces (such as vLLM). The provider selection logic in ai-llm.ts (lines 30–61) dynamically instantiates the appropriate client and chat wrapper.
For Mistral AI, the implementation creates a Mistral client from the @mistralai/mistralai SDK and selects between ChatMistralAI (official API) or ChatOpenAI (OpenAI-compatible endpoints):
if (AI_ENABLED && AI_TOKEN) {
switch (AI_TYPE) {
case 'mistralai':
client = new Mistral({
serverURL: isEmptyField(AI_ENDPOINT) ? undefined : AI_ENDPOINT,
apiKey: AI_TOKEN,
});
if (AI_ENDPOINT?.includes('https://api.mistral.ai')) {
nlqChat = new ChatMistralAI({
model: AI_MODEL,
apiKey: AI_TOKEN,
temperature: 0
});
} else {
nlqChat = new ChatOpenAI({
model: AI_MODEL,
apiKey: AI_TOKEN,
temperature: 0,
configuration: { baseURL: `${AI_ENDPOINT}/v1` },
});
}
break;
}
}
Streaming Query Implementation
The queryMistralAi function in ai-llm.ts (lines 104–132) handles streaming chat completions. It constructs a ChatCompletionStreamRequest, sends it to the Mistral client, and processes the response stream chunk by chunk:
export const queryMistralAi = async (
busId: string | null,
systemMessage: string,
userMessage: string,
user: AuthUser,
) => {
if (!client) throw UnsupportedError('Incorrect AI configuration');
const request: ChatCompletionStreamRequest = {
model: AI_MODEL,
temperature: 0,
messages: [
{ role: 'system', content: systemMessage },
{ role: 'user', content: truncate(userMessage, AI_MAX_TOKENS, false) },
],
};
const response = await (client as Mistral)?.chat.stream(request);
let content = '';
for await (const chunk of response) {
if (chunk.data.choices[0].delta.content !== undefined) {
content += chunk.data.choices[0].delta.content;
if (busId !== null) {
await notify(BUS_TOPICS[AI_BUS].EDIT_TOPIC, { bus_id: busId, content }, user);
}
}
}
return content;
};
This implementation supports real-time updates through OpenCTI's internal event bus when a busId is provided.
Unified AI Query Interface
The queryAi function (lines 184–195) serves as the unified entry point for all AI operations. It routes requests to provider-specific implementations based on the AI_TYPE configuration:
export const queryAi = async (
busId: string | null,
developerMessage: string | null,
userMessage: string,
user: AuthUser,
) => {
const finalDeveloperMessage = developerMessage
|| 'You are an assistant helping a cyber threat intelligence analyst …';
switch (AI_TYPE) {
case 'mistralai':
return queryMistralAi(busId, finalDeveloperMessage, userMessage, user);
case 'azureopenai':
case 'openai':
return queryChatGpt(busId, finalDeveloperMessage, userMessage, user);
default:
throw UnsupportedError('Not supported AI type', { type: AI_TYPE });
}
};
GraphQL API Exposure
High-level AI features in opencti-platform/opencti-graphql/src/modules/ai/ai-domain.ts consume the unified interface. Functions like fixSpelling, summarize, and generateNLQresponse call queryAi (or queryNLQAi for natural language queries) and expose capabilities through GraphQL mutations.
For example, the spell-checking resolver (lines 57–74) constructs a prompt and streams the correction:
export const fixSpelling = async (context, user, id, content, format = Format.Text) => {
const prompt = `...${content}`;
const response = await queryAi(id, SYSTEM_PROMPT, prompt, user);
return response;
};
Clients invoke these capabilities via GraphQL mutations:
mutation FixSpelling($id: ID!, $content: String!, $format: Format) {
fixSpelling(id: $id, content: $content, format: $format)
}
Practical Configuration for Mistral AI
To enable OpenCTI Mistral AI integration, configure your config.yml as follows:
ai:
enabled: true
type: mistralai
endpoint: https://api.mistral.ai
token: ${MISTRAL_API_KEY}
model: mistral-large
max_tokens: 2048
Set the API key via environment variable:
export MISTRAL_API_KEY=your-mistral-api-key
After restarting the platform, AI-powered mutations become available through the GraphQL API.
Summary
- OpenCTI implements a provider-agnostic AI layer supporting Mistral AI, OpenAI, and Azure OpenAI through unified configuration keys in
ai-llm.ts. - The platform dynamically selects between
ChatMistralAIandChatOpenAIwrappers based on endpoint URL patterns, enabling both official APIs and OpenAI-compatible deployments. - Streaming responses are handled through
queryMistralAi, which processes chunks from the Mistral client and optionally pushes updates through the internal event bus. - GraphQL resolvers in
ai-domain.tsexpose AI capabilities (spelling correction, summarization, natural language queries) without hardcoding provider logic. - All AI features route through the
queryAidispatcher, ensuring consistent error handling and system prompt management across providers.
Frequently Asked Questions
How do I configure OpenCTI to use a self-hosted Mistral model via vLLM?
Set ai:type to mistralai and point ai:endpoint to your vLLM server URL. The code in ai-llm.ts detects non-official endpoints and automatically uses ChatOpenAI with a custom baseURL instead of the native ChatMistralAI client, allowing OpenAI-compatible API access to your local model.
What GraphQL mutations are available for AI features in OpenCTI?
Available mutations include fixSpelling for text correction, summarize for content condensation, and generateNLQresponse for natural language querying. These reside in ai-domain.ts and accept parameters like entity IDs, content strings, and output formats, returning AI-generated responses streamed from your configured provider.
Where does OpenCTI handle AI authentication and token management?
Authentication tokens are read from the nconf configuration system in ai-llm.ts (lines 18–27) via conf.get('ai:token'). The platform passes these tokens directly to provider SDKs (Mistral, OpenAI) without logging or exposing them in error messages, ensuring secure credential handling.
Can OpenCTI stream AI responses to connected clients in real-time?
Yes. When a busId is provided to queryMistralAi, the function calls notify() on the internal event bus (BUS_TOPICS[AI_BUS].EDIT_TOPIC) for each content chunk received from the Mistral stream. This enables live updates in the OpenCTI web interface or other subscribed clients during long-running AI operations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →