integration
Architecture
Studio is a native component of QALITA Platform, not a separate package.
Platform Architecture
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Platform Web │ REST │ Platform API │ gRPC │ Worker │
│ (Frontend) │ ←──────→ │ (Backend) │ ←──────→ │ │
│ │ │ │ │ │
│ - Studio UI │ │ - Studio API │ │ - Data Access │
│ - Chat Panel │ │ - LLM Agent │ │ - SQL Queries │
│ - Context Bar │ │ - Data Tools │ │ - File Reads │
└─────────────────┘ └─────────────────┘ └─────────────────┘
React/Next.js FastAPI + LangGraph Python CLI
Component Responsibilities
| Component | Responsibility |
|---|---|
| Frontend | Chat interface, context selection, streaming display |
| Backend Agent | LLM orchestration, tool execution, response generation |
| Backend API | REST endpoints, authentication, data preview |
| Worker | Source connectivity, query execution, data retrieval |
Data Flow
- User sends message → Frontend collects context (issue, source, schema, metrics)
- API receives request → Creates LLM instance from database config
- Agent processes → Invokes LangGraph workflow, may call data tools
- Tools execute → gRPC requests to Worker for data operations
- Response streams → SSE events back to frontend
LLM Configuration
LLM providers are configured through the Platform database, not environment variables or files.
Database Model
LLM configurations are stored in the llm_configuration table:
| Column | Type | Description |
|---|---|---|
id | int | Primary key |
partner_id | int | Organization FK |
name | str | Display name |
provider | str | Provider type |
model_name | str | Model identifier |
api_key | str | Encrypted API key |
endpoint_url | str | Custom endpoint |
is_active | bool | Active configuration |
configuration | json | Additional parameters |
Provider Mapping
The backend maps Platform providers to LangChain providers:
| Platform Provider | LangChain Provider |
|---|---|
openai | openai |
azure_openai | azure_openai |
anthropic | anthropic |
ollama | ollama |
mistral | mistral |
generic | openai (compatible API) |
Creating LLM from Config
The backend uses a factory pattern to create LLM instances:
from backend.agent import create_llm_from_db_config
# Async function to create LLM from active config
llm = await create_llm_from_db_config(
session=db_session,
partner_id=current_user.partner_id,
config_id=None, # Uses active config if None
streaming=True, # Enable streaming
)
Default Models
If no model is specified, defaults are used:
| Provider | Default Model |
|---|---|
| OpenAI | gpt-4o-mini |
| Azure OpenAI | gpt-4o-mini |
| Anthropic | claude-3-haiku-20240307 |
| Ollama | llama3.2 |
| Mistral | mistral-small-latest |
Status & Capabilities
Worker Connection Status
Endpoint: GET /api/v1/studio/status
Returns information about connected Workers for data operations:
{
"connected_workers": [1, 3, 5],
"worker_count": 3
}
Requirements: At least one Worker must be connected for data tools to function.
Agent Capabilities
Endpoint: GET /api/v1/studio/agent/capabilities
Returns agent availability and LLM configuration status:
{
"agent_available": true,
"llm_configured": true,
"active_config": {
"id": 1,
"name": "Production GPT-4o",
"provider": "openai",
"model_name": "gpt-4o",
"endpoint_url": null
}
}
States:
agent_available | llm_configured | Meaning |
|---|---|---|
true | true | Fully operational |
true | false | Agent ready, needs LLM config |
false | * | Install langchain/langgraph |
Visual Indicators
In the Platform frontend, indicators show:
- 🟢 Agent Ready: LLM configured and agent available
- 🟡 Partial: Agent available but no LLM config
- 🔴 Unavailable: Agent module not installed
- ⚪ No Worker: No workers connected for data access
Data Preview Integration
Studio can preview source data through Workers using gRPC.
Preview Architecture
Frontend → Backend API → Worker (gRPC) → Source (DB/File)
↓
DataPreviewManager
↓
Response correlation
Preview Request Flow
- Frontend requests preview for source ID
- Backend verifies source belongs to user's partner
- Backend finds available Worker for partner
- Backend creates gRPC
DataPreviewRequest - Worker reads data from source
- Worker returns
DataPreviewResponsevia gRPC - Backend returns formatted JSON to frontend
Preview Endpoint
GET /api/v1/studio/sources/{source_id}/preview
Parameters:
limit: Max rows (1-10000, default 1000)source_version_id: Specific versionquery: Custom SQL (for database sources)
Response:
{
"ok": true,
"data_type": "table",
"headers": ["id", "name", "email", "created_at"],
"rows": [
{"values": ["1", "John Doe", "john@example.com", "2024-01-15"]},
{"values": ["2", "Jane Smith", "jane@example.com", "2024-01-16"]}
],
"total_rows": 10000
}
Supported Data Types
| Type | Description | Content |
|---|---|---|
table | Structured data | headers, rows |
text | Plain text | content |
json | JSON data | content |
image | Binary image | binary_base64, mime_type |
pdf | PDF document | binary_base64, mime_type |
error | Error state | error |
Source & Issue Context
Studio provides contextual conversation retrieval based on sources and issues.
Context Query
GET /api/v1/studio/conversations/context
Parameters:
source_id: Get conversations from issues linked to sourceissue_id: Get conversations from specific issueproject_id: Get all project conversations
Response:
{
"source_conversations": [...],
"issue_conversations": [...],
"project_conversations": [...],
"total_count": 15
}
Conversation Linking
Conversations are linked to Platform entities:
| Field | Purpose |
|---|---|
issue_id | Link to quality issue |
source_id | Link to data source |
project_id | Link to project |
This enables:
- Retrieving conversation history when reopening an issue
- Seeing related conversations from same source
- Project-wide conversation overview
Data Tools Integration
Studio's agent can execute data operations through Workers.
Tool Architecture
Agent (LangGraph) → Tool Function → gRPC → Worker → Source
↓
AgentActionManager
↓
Response correlation
Available Tools
Tools are created dynamically based on the user's context:
from backend.agent.tools import create_data_tools
# Create tools bound to partner
tools = create_data_tools(
partner_id=current_user.partner_id,
source_id=context.get("source_id")
)
Tool Definitions
| Tool | Input Schema | Action |
|---|---|---|
execute_sql_query | source_id, sql, limit | Run SQL on database |
read_source_data | source_id, limit, table, columns | Read data |
describe_source | source_id, table | Get metadata |
sample_source_data | source_id, n, table | Random sample |
count_rows | source_id, table, condition | Count rows |
filter_data | source_id, condition, limit | Filter data |
aggregate_data | source_id, group_by, agg_func | Aggregate |
gRPC Communication
Tools communicate with Workers via gRPC:
Request:
message AgentActionRequest {
string request_id = 1;
string action_type = 2;
int32 source_id = 3;
string parameters_json = 4;
optional int32 timeout_seconds = 5;
}
Response:
message AgentActionResponse {
string request_id = 1;
bool ok = 2;
string action_type = 3;
optional string error = 4;
optional string result_json = 5;
optional DataPreview data = 6;
optional int64 execution_time_ms = 7;
}
Tool Response Formatting
Results are formatted for LLM consumption:
Execution time: 45ms
Columns: id, name, email, created_at
Rows (20 shown, 1000 total):
id | name | email | created_at
------------------------------------
1 | John Doe | john@ex.com | 2024-01-15
2 | Jane Smith | | 2024-01-16
...
Authentication and Security
Authentication Flow
Studio uses Platform's standard authentication:
1. User logs into Platform
↓
2. JWT token issued by Platform auth
↓
3. Frontend includes token in requests
↓
4. Backend validates token, extracts user/partner
↓
5. Studio endpoints check permissions
Required Permissions
| Endpoint | Required Scope |
|---|---|
/api/v1/studio/status | source:get |
/api/v1/studio/sources/{id}/preview | source:get |
/api/v1/studio/agent/capabilities | source:get |
/api/v1/studio/chat | source:get |
/api/v1/studio/conversations/context | issue:get |
Data Isolation
- Partner Isolation: All queries filter by
partner_id - Source Access: Only sources belonging to partner
- Issue Access: Only issues belonging to partner
- Conversations: Only conversations belonging to partner
API Key Security
LLM API keys are:
- Stored encrypted in database
- Never exposed in API responses
- Only used server-side for LLM calls
MCP Server Integration
Studio supports Model Context Protocol (MCP) servers for extended tool capabilities. MCP allows you to connect external tools and services to Studio, expanding what the AI agent can do beyond built-in data tools.
What is MCP?
MCP (Model Context Protocol) is an open standard that allows AI applications to connect with external tools and data sources. By adding MCP servers to Studio, you can extend the agent with custom capabilities such as:
- Custom data analysis tools
- Domain-specific validation functions
- External API integrations
- Specialized computation services
Setting Up an MCP Server
Step 1: Navigate to AI Settings
Go to Settings > AI Configuration > MCP Servers in the Platform.
Step 2: Add a New MCP Server
Click Add MCP Server and fill in the configuration:
| Field | Description | Example |
|---|---|---|
| Name | A descriptive name for the server | "Data Validation Tools" |
| Endpoint URL | The HTTP/SSE endpoint of the MCP server | https://mcp.example.com/rpc |
| API Key | Optional authentication key | sk-... |
| Active | Whether the server is enabled | true |
Step 3: Refresh Tools
After adding the server, click Refresh Tools to discover the available tools exposed by the MCP server. The discovered tools will be cached and displayed in the configuration.
Step 4: Use in Studio
Once configured and active, MCP tools are automatically available in Studio conversations. The AI agent can invoke them when relevant to the user's query. MCP tools are prefixed with mcp_ to distinguish them from built-in tools.
MCP Architecture
Agent → LangChain Tool → MCPHttpClient → MCP Server
↓
JSON-RPC 2.0
Technical Details
MCP servers are configured per organization in the database:
{
"name": "Data Tools Server",
"endpoint_url": "https://mcp.example.com/rpc",
"api_key": "...",
"is_active": true,
"tools_cache": [
{"name": "analyze_csv", "description": "..."},
{"name": "validate_schema", "description": "..."}
]
}
MCP tools are automatically converted to LangChain tools at runtime:
from backend.agent.mcp_client import create_langchain_tools_from_mcp_servers
# Get all active MCP tools for partner
mcp_tools = await create_langchain_tools_from_mcp_servers(
session=db_session,
partner_id=current_user.partner_id
)
# Tools are prefixed with "mcp_" and include server name in description
# e.g., "mcp_analyze_csv" with description "[MCP:DataTools] Analyze CSV file"
Troubleshooting MCP
| Issue | Solution |
|---|---|
| Tools not appearing | Click Refresh Tools in the MCP server configuration |
| Connection refused | Verify the endpoint URL is accessible from the Platform backend |
| Authentication error | Check the API key is correct and has not expired |
| Tool execution timeout | Verify the MCP server is running and responsive |
Troubleshooting
"Agent module not available"
Cause: LangChain/LangGraph not installed
Solution:
# On the Platform backend server
pip install langchain langgraph langchain-openai langchain-anthropic langchain-ollama
"No LLM configuration found"
Cause: No active LLM config for partner
Solution:
- Go to Settings > AI Configuration
- Create a new configuration
- Set it as Active
"No worker available"
Cause: No Worker connected for data operations
Solution:
# Start a Worker
qalita worker start
Worker timeout
Cause: Worker didn't respond in time
Checks:
- Verify Worker is running and connected
- Check Worker logs for errors
- Verify source connectivity from Worker
- Increase timeout if needed
LLM connection errors
OpenAI:
curl https://api.openai.com/v1/models \
-H "Authorization: Bearer YOUR_KEY"
Ollama:
curl http://localhost:11434/api/tags
Check error details in response:
{
"ok": false,
"error": "Connection to Ollama refused at http://localhost:11434"
}
Monitoring
Backend Logs
Studio operations are logged with loguru:
# In backend logs
INFO | backend.agent.agent:invoke:505 - Invoking agent workflow
INFO | backend.agent.tools.data_tools:execute_query_tool:178 - Executing SQL query on source 456
DEBUG | backend.agent.llm_factory:create_chat_model:120 - Creating LLM with provider=openai, model=gpt-4o-mini
Metrics to Monitor
| Metric | Description |
|---|---|
| Chat request latency | Time from request to response |
| Streaming first byte | Time to first SSE event |
| Tool execution time | Duration of data tool calls |
| Worker response time | gRPC round-trip time |
| LLM token usage | Tokens consumed per request |
Complete Workflow Example
import requests
import json
BASE_URL = "https://your-platform/api/v1"
HEADERS = {"Authorization": "Bearer YOUR_TOKEN"}
# 1. Check agent capabilities
caps = requests.get(f"{BASE_URL}/studio/agent/capabilities", headers=HEADERS).json()
print(f"Agent available: {caps['agent_available']}, LLM configured: {caps['llm_configured']}")
# 2. Check worker status
status = requests.get(f"{BASE_URL}/studio/status", headers=HEADERS).json()
print(f"Workers connected: {status['worker_count']}")
# 3. Preview source data
preview = requests.get(
f"{BASE_URL}/studio/sources/456/preview",
headers=HEADERS,
params={"limit": 100}
).json()
print(f"Columns: {preview['headers']}")
# 4. Chat with context
response = requests.post(
f"{BASE_URL}/studio/chat",
headers=HEADERS,
json={
"message": "Analyze the data quality of this source",
"context": {
"source_id": 456,
"enriched": {
"source": {"id": 456, "name": "customers", "type": "postgresql"},
"dataSample": preview
}
}
}
).json()
print(f"Agent response: {response['response'][:500]}...")
# 5. Get related conversations
convs = requests.get(
f"{BASE_URL}/studio/conversations/context",
headers=HEADERS,
params={"source_id": 456}
).json()
print(f"Related conversations: {convs['total_count']}")
Best Practices
-
LLM Configuration:
- Start with Ollama for development (free, private)
- Use
gpt-4o-minifor cost-effective production - Test configurations before activating
-
Worker Management:
- Ensure at least one Worker per environment
- Monitor Worker connectivity
- Handle Worker unavailability gracefully
-
Context Enrichment:
- Always include relevant context (source, issue)
- Use data samples when asking about data
- Include schema for structure questions
-
Error Handling:
- Check
okfield in responses - Handle streaming errors in SSE
- Implement retry logic for transient failures
- Check
Next Steps
- 🚀 Features - Explore all Studio capabilities
- 💬 Conversation Management - Organize your conversations
- 📖 Configuration - Configure LLM providers