l-ai-core-proto/ai.proto
Kaien ea73b19a88 feat(proto): GenerateAnswer image response and intent classification fields.
Add user_message on AIRequest, ResponseKind/image bytes on AIResponse, and
image_prompt so clients can persist image context in chat history.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-01 01:10:12 +03:00

313 lines
18 KiB
Protocol Buffer
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

syntax = "proto3";
package ai;
option go_package = "git.be.kaien.ru/l-ai/l-ai-core-proto/proto";
// AIRequest содержит запрос к AI модели
message AIRequest {
string text = 1; // Текст запроса
string bot_username = 2; // Username бота для упоминаний
string model_name = 3; // Название модели (по умолчанию "deepseek-v4-flash")
bool enable_reasoning = 4; // Включить режим рассуждений (по умолчанию false)
int64 global_user_id = 5; // Global user ID для получения доп. данных (профиль, воспоминания)
AnswerGenerationOptions options = 6; // Дополнительные опции генерации ответа
int64 telegram_user_id = 7; // Telegram user ID для применения пользовательских настроек (стиль ответа)
int64 steam_user_id = 8; // SteamID64 отправителя для живых Steam-диалогов (l-ai-steam) - тот же "сигнал живого чата", что telegram_user_id, для второй платформы
bool supports_deferred = 9; // Клиент умеет обрабатывать status=PENDING + GetAnswerJob (async тяжёлые тулзы). Старые клиенты не ставят — тяжёлые тулзы исполняются синхронно, как раньше.
// Структурированные воспоминания, которые клиент уже вложил в text (для debug-trace).
// Core сохраняет их as-is на ai_response_traces — не перезапрашивает memories-сервис.
repeated UsedMemory used_memories = 10;
// Raw user message for intent classification (no history/memories/tools).
// Live chat clients (Telegram/Steam) send this; when empty, image intent
// detection is skipped and the request is treated as normal chat.
string user_message = 11;
}
// UsedMemory — снимок одного воспоминания, участвовавшего в генерации ответа.
message UsedMemory {
int64 id = 1; // ID в l-ai-memories (для будущего delete с debug-страницы)
int64 user_id = 2;
string username = 3;
string key = 4;
string value = 5;
string category = 6;
string priority = 7;
string source_label = 8; // контекст от клиента, напр. "автор сообщения (@nick)"
}
// AnswerGenerationOptions содержит дополнительные опции для генерации ответа
message AnswerGenerationOptions {
bool include_user_memories = 1; // Включить воспоминания о пользователе в промпт
}
// ResponseKind — тип синхронного ответа GenerateAnswer (кроме deferred jobs).
enum ResponseKind {
RESPONSE_KIND_UNSPECIFIED = 0; // трактуется как TEXT (обратная совместимость)
RESPONSE_KIND_TEXT = 1; // content — текстовый ответ
RESPONSE_KIND_IMAGE = 2; // image_base64 — сгенерированное изображение
}
// AIResponse содержит ответ от AI модели
message AIResponse {
string content = 1; // Текст ответа (при status=PENDING — interim-сообщение)
string error = 2; // Текст ошибки, если произошла
double duration_ms = 3; // Время выполнения в миллисекундах
string reasoning_content = 4; // Текст рассуждений (если включён режим reasoning)
AnswerStatus status = 5; // COMPLETE (или UNSPECIFIED для старых клиентов) — content финальный;
// PENDING — запущена долгая job, content это interim, финал тянуть через GetAnswerJob(job_id)
string job_id = 6; // Идентификатор отложенной job (только при status=PENDING)
string response_id = 7; // UUID (hex) debug-trace; доступен уже при PENDING
ResponseKind kind = 8; // TEXT (default) или IMAGE
string image_base64 = 9; // base64-encoded image bytes when kind=IMAGE
string image_mime_type = 10; // e.g. image/png when kind=IMAGE
string image_prompt = 11; // final Muse Image prompt when kind=IMAGE (for client-side history)
}
// AnswerStatus описывает, готов ли ответ синхронно или отложен в job.
enum AnswerStatus {
ANSWER_STATUS_UNSPECIFIED = 0; // трактуется как COMPLETE (обратная совместимость)
ANSWER_STATUS_COMPLETE = 1; // content — финальный ответ
ANSWER_STATUS_PENDING = 2; // запущена долгая job; поллить GetAnswerJob(job_id)
}
// GetAnswerJobRequest запрашивает статус отложенной генерации ответа.
message GetAnswerJobRequest {
string job_id = 1;
}
// AnswerJobState — состояние отложенной генерации ответа.
// Значения 0-4 исторические (GenerateAnswer deferred). Значения 5-7 добавлены для
// durable RunAgent jobs; старые клиенты их не создают и потому не увидят. RUNNING
// для старого клиента семантически эквивалентен PENDING (job ещё не терминальна).
enum AnswerJobState {
ANSWER_JOB_STATE_UNSPECIFIED = 0;
ANSWER_JOB_STATE_PENDING = 1; // ещё не взята в работу / выполняется (evolver работает, модель дописывает)
ANSWER_JOB_STATE_DONE = 2; // готово, content заполнен
ANSWER_JOB_STATE_FAILED = 3; // ошибка, error заполнен
ANSWER_JOB_STATE_NOT_FOUND = 4; // неизвестный/протухший job_id (напр. после рестарта core)
ANSWER_JOB_STATE_RUNNING = 5; // взята воркером (lease активен), tool loop исполняется
ANSWER_JOB_STATE_CANCELLED = 6; // отменена через CancelAnswerJob
ANSWER_JOB_STATE_EXPIRED = 7; // истёк expiration до достижения терминального состояния
}
// AnswerJobResponse — результат опроса отложенной генерации.
message AnswerJobResponse {
AnswerJobState state = 1;
string content = 2; // финальный ответ, когда DONE
string error = 3; // причина, когда FAILED
double duration_ms = 4; // суммарное время генерации, когда DONE
// Authoritative usage завершённой deferred RunAgent job — тот же счётчик, что
// возвращает synchronous RunAgent (реальные iterations/tool calls core, не
// self-reported из ответа модели). Для legacy GenerateAnswer jobs не заполняется.
AgentUsage agent_usage = 5;
string response_id = 6; // тот же debug-trace id, что в AIResponse при PENDING
}
// ToolCallTrace — один вызов тула во время GenerateAnswer (args + результат для модели).
message ToolCallTrace {
string call_id = 1;
string name = 2;
string arguments_json = 3;
string result = 4;
string error = 5;
bool deferred = 6; // тяжёлый тул через l-evolver
}
// GetAIResponseTraceRequest — запрос debug-снимка генерации по UUID.
message GetAIResponseTraceRequest {
string id = 1; // response_id (hex UUID без дефисов)
}
// GetAIResponseTraceResponse — снимок того, что ушло в модель / вернулось.
message GetAIResponseTraceResponse {
string id = 1;
string bot_username = 2;
string model_name = 3;
int64 telegram_user_id = 4;
int64 global_user_id = 5;
string system_prompt = 6;
string user_prompt = 7;
repeated UsedMemory memories = 8;
repeated ToolCallTrace tool_calls = 9;
string response = 10;
string reasoning_content = 11;
string error = 12;
double duration_ms = 13;
int64 created_at_unix = 14;
bool found = 15; // false — нет такого id / истёк TTL
}
// CreateSessionRequest для создания новой AI сессии
message CreateSessionRequest {
string user_id = 1; // Идентификатор пользователя
string bot_username = 2; // Username бота для упоминаний
string model_name = 3; // Название модели (по умолчанию "deepseek-v4-flash")
int64 ttl_seconds = 4; // Время жизни сессии в секундах (по умолчанию 3600 = 1 час)
}
// CreateSessionResponse содержит ID созданной сессии
message CreateSessionResponse {
string session_id = 1; // ID сессии
string status = 2; // Статус сессии
string error = 3; // Текст ошибки, если произошла
}
// AskInSessionRequest для запроса в рамках сессии
message AskInSessionRequest {
string session_id = 1; // ID сессии
string text = 2; // Текст запроса
bool enable_reasoning = 3; // Включить режим рассуждений (по умолчанию false)
int64 global_user_id = 4; // Global user ID для получения воспоминаний
AnswerGenerationOptions options = 5; // Дополнительные опции генерации
}
// SessionStatusResponse содержит статус сессии и ответ AI
message SessionStatusResponse {
string session_id = 1; // ID сессии
string status = 2; // Статус сессии (active, expired, closed)
string content = 3; // Текст ответа от AI
string error = 4; // Текст ошибки, если произошла
double duration_ms = 5; // Время выполнения в миллисекундах
string reasoning_content = 6; // Текст рассуждений (если включён режим reasoning)
}
// StatelessMessage одно сообщение для stateless запроса (role + content)
message StatelessMessage {
string role = 1; // "system", "user", "assistant"
string content = 2; // Текст сообщения
}
// StatelessRequest для stateless генерации с полным контролем над messages
message StatelessRequest {
repeated StatelessMessage messages = 1; // Массив сообщений (system + history + user)
string model_name = 2; // Название модели (по умолчанию "deepseek-v4-flash")
}
// HealthCheckRequest для проверки здоровья сервиса
message HealthCheckRequest {}
// HealthCheckResponse для ответа на проверку здоровья
message HealthCheckResponse {
bool serving = 1;
string version = 2;
}
// ---------------------------------------------------------------------------
// RunAgent: надёжный агентный вызов (server-side tool filtering, deferred jobs).
// В отличие от GenerateAnswer, НЕ подмешивает Telegram/Steam-персонализацию и не
// требует telegram_user_id/steam_user_id. Вызывающая сторона полностью владеет
// массивом messages (включая system). Требует service-to-service авторизации
// (Bearer service-token в gRPC metadata) — agent_id/tool_profile сами по себе не
// являются авторизацией.
// ---------------------------------------------------------------------------
// AgentToolLoopOptions задаёт лимиты tool loop. Значения клиента могут только
// сузить серверные потолки (max_tool_iterations/max_total_tool_calls ≤ server max).
message AgentToolLoopOptions {
bool tools_enabled = 1; // false — модель отвечает без tools
uint32 max_tool_iterations = 2; // потолок round-trip'ов модель↔tools (0 = серверный default)
uint32 max_total_tool_calls = 3; // потолок РЕАЛЬНЫХ tool call'ов, не итераций (0 = серверный default)
uint32 tool_timeout_seconds = 4; // таймаут одного tool call (0 = серверный default)
uint32 total_timeout_seconds = 5; // wall-clock таймаут всего вызова (0 = серверный default), независим от budget
}
// AgentOutputFormat — требуемый формат финального ответа.
enum AgentOutputFormat {
AGENT_OUTPUT_FORMAT_UNSPECIFIED = 0; // трактуется как TEXT
AGENT_OUTPUT_FORMAT_TEXT = 1; // произвольный текст
AGENT_OUTPUT_FORMAT_JSON = 2; // должен быть валидный JSON (без schema-проверки)
AGENT_OUTPUT_FORMAT_JSON_SCHEMA = 3; // валидный JSON, проходящий json_schema
}
// AgentResponseContract описывает требования к структурированному ответу.
message AgentResponseContract {
AgentOutputFormat format = 1;
string json_schema = 2; // JSON Schema (draft-07), обязателен при format=JSON_SCHEMA
bool repair_invalid_output = 3; // при провале валидации — один repair-запрос модели (без новых tools)
}
// AgentRequest — запрос агентного вызова.
message AgentRequest {
repeated StatelessMessage messages = 1; // полный контекст, включая system
string model_name = 2; // модель (по умолчанию deepseek-v4-flash)
bool enable_reasoning = 3; // reasoning не попадает в обычный content
string agent_id = 4; // идентификатор логического агента (для логов/метрик, НЕ авторизация)
string tool_profile = 5; // серверный профиль tools (НЕ авторизация; проверяется против credential)
repeated string requested_tools = 6; // сужение профиля: effective = profile.allowed ∩ requested; пусто = все allowed
AgentToolLoopOptions tool_loop = 7;
AgentResponseContract response_contract = 8;
string request_id = 9; // сквозной идентификатор для трассировки
string idempotency_key = 10; // повторный RunAgent с тем же ключом не создаёт дубликат job
bool supports_deferred = 11; // клиент умеет status=PENDING + GetAnswerJob для тяжёлых операций
map<string, string> metadata = 12; // произвольные метки (не логируются как секреты)
}
// AgentUsage — счётчики использования за вызов.
message AgentUsage {
uint32 tool_iterations = 1; // сколько round-trip'ов модель↔tools произошло
uint32 total_tool_calls = 2; // сколько РЕАЛЬНЫХ tool call'ов исполнено (3 calls в одной итерации = 3)
repeated string tools_used = 3;
uint64 input_tokens = 4;
uint64 output_tokens = 5;
}
// AgentResponse — результат агентного вызова.
message AgentResponse {
AnswerStatus status = 1; // COMPLETE — content финальный; PENDING — тянуть через GetAnswerJob(job_id)
string content = 2;
string error = 3;
double duration_ms = 4;
string job_id = 5; // при status=PENDING
AgentUsage usage = 6;
}
// CancelAnswerJobRequest — запрос отмены отложенной job (идемпотентный).
message CancelAnswerJobRequest {
string job_id = 1;
string reason = 2; // причина отмены (сохраняется), опционально
}
// CancelAnswerJobResponse — результат отмены.
message CancelAnswerJobResponse {
AnswerJobState state = 1; // состояние job после запроса (CANCELLED, либо терминальное, если уже завершилась)
bool cancelled = 2; // true — отмена применена этим вызовом; false — job уже была терминальна/отменена
string error = 3;
}
// AIService - основной сервис для работы с AI
service AIService {
// RunAgent — надёжный агентный вызов с серверной фильтрацией tools и deferred
// jobs. Требует service-to-service авторизации.
rpc RunAgent(AgentRequest) returns (AgentResponse);
// CancelAnswerJob отменяет отложенную job (идемпотентно). Требует авторизации.
rpc CancelAnswerJob(CancelAnswerJobRequest) returns (CancelAnswerJobResponse);
// GenerateAnswer генерирует ответ на запрос
rpc GenerateAnswer(AIRequest) returns (AIResponse);
// GenerateStateless генерирует ответ из массива messages (system+history+user)
rpc GenerateStateless(StatelessRequest) returns (AIResponse);
// HealthCheck проверяет здоровье сервиса
rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse);
// CreateSession созда новую AI сессию
rpc CreateSession(CreateSessionRequest) returns (CreateSessionResponse);
// AskInSession отправляет запрос в рамках существующей сессии
rpc AskInSession(AskInSessionRequest) returns (SessionStatusResponse);
// GetAnswerJob возвращает статус отложенной генерации ответа (когда GenerateAnswer
// вернул status=PENDING). Клиент поллит его до DONE/FAILED и постит финал.
rpc GetAnswerJob(GetAnswerJobRequest) returns (AnswerJobResponse);
// GetAIResponseTrace возвращает debug-снимок генерации (промпты, memories, tool calls).
rpc GetAIResponseTrace(GetAIResponseTraceRequest) returns (GetAIResponseTraceResponse);
}