From 00e8f9b190355eb5ba0e88ffd74626ed964d9b58 Mon Sep 17 00:00:00 2001 From: Kaien Date: Wed, 5 Aug 2026 23:07:59 +0300 Subject: [PATCH] feat(proto): add RunAgent, CancelAnswerJob, Agent* messages Extend AnswerJobState with RUNNING/CANCELLED/EXPIRED (values 5-7, backward compatible). Add AgentRequest/AgentResponse/AgentToolLoopOptions/ AgentResponseContract/AgentUsage/AgentOutputFormat and Cancel* messages. Existing RPCs unchanged. Co-Authored-By: Claude Opus 4.8 --- ai.pb.go | 915 ++++++++++++++++++++++++++++++++++++++++++++------ ai.proto | 101 +++++- ai_grpc.pb.go | 86 ++++- 3 files changed, 990 insertions(+), 112 deletions(-) diff --git a/ai.pb.go b/ai.pb.go index 34ed266..7e5ec30 100644 --- a/ai.pb.go +++ b/ai.pb.go @@ -2,7 +2,7 @@ // versions: // protoc-gen-go v1.36.11 // protoc v7.34.0 -// source: proto/l-ai-core-proto/ai.proto +// source: ai.proto package proto @@ -55,11 +55,11 @@ func (x AnswerStatus) String() string { } func (AnswerStatus) Descriptor() protoreflect.EnumDescriptor { - return file_proto_l_ai_core_proto_ai_proto_enumTypes[0].Descriptor() + return file_ai_proto_enumTypes[0].Descriptor() } func (AnswerStatus) Type() protoreflect.EnumType { - return &file_proto_l_ai_core_proto_ai_proto_enumTypes[0] + return &file_ai_proto_enumTypes[0] } func (x AnswerStatus) Number() protoreflect.EnumNumber { @@ -68,18 +68,24 @@ func (x AnswerStatus) Number() protoreflect.EnumNumber { // Deprecated: Use AnswerStatus.Descriptor instead. func (AnswerStatus) EnumDescriptor() ([]byte, []int) { - return file_proto_l_ai_core_proto_ai_proto_rawDescGZIP(), []int{0} + return file_ai_proto_rawDescGZIP(), []int{0} } // AnswerJobState — состояние отложенной генерации ответа. +// Значения 0-4 исторические (GenerateAnswer deferred). Значения 5-7 добавлены для +// durable RunAgent jobs; старые клиенты их не создают и потому не увидят. RUNNING +// для старого клиента семантически эквивалентен PENDING (job ещё не терминальна). type AnswerJobState int32 const ( AnswerJobState_ANSWER_JOB_STATE_UNSPECIFIED AnswerJobState = 0 - AnswerJobState_ANSWER_JOB_STATE_PENDING AnswerJobState = 1 // ещё выполняется (evolver работает / модель дописывает) + AnswerJobState_ANSWER_JOB_STATE_PENDING AnswerJobState = 1 // ещё не взята в работу / выполняется (evolver работает, модель дописывает) AnswerJobState_ANSWER_JOB_STATE_DONE AnswerJobState = 2 // готово, content заполнен AnswerJobState_ANSWER_JOB_STATE_FAILED AnswerJobState = 3 // ошибка, error заполнен AnswerJobState_ANSWER_JOB_STATE_NOT_FOUND AnswerJobState = 4 // неизвестный/протухший job_id (напр. после рестарта core) + AnswerJobState_ANSWER_JOB_STATE_RUNNING AnswerJobState = 5 // взята воркером (lease активен), tool loop исполняется + AnswerJobState_ANSWER_JOB_STATE_CANCELLED AnswerJobState = 6 // отменена через CancelAnswerJob + AnswerJobState_ANSWER_JOB_STATE_EXPIRED AnswerJobState = 7 // истёк expiration до достижения терминального состояния ) // Enum value maps for AnswerJobState. @@ -90,6 +96,9 @@ var ( 2: "ANSWER_JOB_STATE_DONE", 3: "ANSWER_JOB_STATE_FAILED", 4: "ANSWER_JOB_STATE_NOT_FOUND", + 5: "ANSWER_JOB_STATE_RUNNING", + 6: "ANSWER_JOB_STATE_CANCELLED", + 7: "ANSWER_JOB_STATE_EXPIRED", } AnswerJobState_value = map[string]int32{ "ANSWER_JOB_STATE_UNSPECIFIED": 0, @@ -97,6 +106,9 @@ var ( "ANSWER_JOB_STATE_DONE": 2, "ANSWER_JOB_STATE_FAILED": 3, "ANSWER_JOB_STATE_NOT_FOUND": 4, + "ANSWER_JOB_STATE_RUNNING": 5, + "ANSWER_JOB_STATE_CANCELLED": 6, + "ANSWER_JOB_STATE_EXPIRED": 7, } ) @@ -111,11 +123,11 @@ func (x AnswerJobState) String() string { } func (AnswerJobState) Descriptor() protoreflect.EnumDescriptor { - return file_proto_l_ai_core_proto_ai_proto_enumTypes[1].Descriptor() + return file_ai_proto_enumTypes[1].Descriptor() } func (AnswerJobState) Type() protoreflect.EnumType { - return &file_proto_l_ai_core_proto_ai_proto_enumTypes[1] + return &file_ai_proto_enumTypes[1] } func (x AnswerJobState) Number() protoreflect.EnumNumber { @@ -124,7 +136,60 @@ func (x AnswerJobState) Number() protoreflect.EnumNumber { // Deprecated: Use AnswerJobState.Descriptor instead. func (AnswerJobState) EnumDescriptor() ([]byte, []int) { - return file_proto_l_ai_core_proto_ai_proto_rawDescGZIP(), []int{1} + return file_ai_proto_rawDescGZIP(), []int{1} +} + +// AgentOutputFormat — требуемый формат финального ответа. +type AgentOutputFormat int32 + +const ( + AgentOutputFormat_AGENT_OUTPUT_FORMAT_UNSPECIFIED AgentOutputFormat = 0 // трактуется как TEXT + AgentOutputFormat_AGENT_OUTPUT_FORMAT_TEXT AgentOutputFormat = 1 // произвольный текст + AgentOutputFormat_AGENT_OUTPUT_FORMAT_JSON AgentOutputFormat = 2 // должен быть валидный JSON (без schema-проверки) + AgentOutputFormat_AGENT_OUTPUT_FORMAT_JSON_SCHEMA AgentOutputFormat = 3 // валидный JSON, проходящий json_schema +) + +// Enum value maps for AgentOutputFormat. +var ( + AgentOutputFormat_name = map[int32]string{ + 0: "AGENT_OUTPUT_FORMAT_UNSPECIFIED", + 1: "AGENT_OUTPUT_FORMAT_TEXT", + 2: "AGENT_OUTPUT_FORMAT_JSON", + 3: "AGENT_OUTPUT_FORMAT_JSON_SCHEMA", + } + AgentOutputFormat_value = map[string]int32{ + "AGENT_OUTPUT_FORMAT_UNSPECIFIED": 0, + "AGENT_OUTPUT_FORMAT_TEXT": 1, + "AGENT_OUTPUT_FORMAT_JSON": 2, + "AGENT_OUTPUT_FORMAT_JSON_SCHEMA": 3, + } +) + +func (x AgentOutputFormat) Enum() *AgentOutputFormat { + p := new(AgentOutputFormat) + *p = x + return p +} + +func (x AgentOutputFormat) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (AgentOutputFormat) Descriptor() protoreflect.EnumDescriptor { + return file_ai_proto_enumTypes[2].Descriptor() +} + +func (AgentOutputFormat) Type() protoreflect.EnumType { + return &file_ai_proto_enumTypes[2] +} + +func (x AgentOutputFormat) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use AgentOutputFormat.Descriptor instead. +func (AgentOutputFormat) EnumDescriptor() ([]byte, []int) { + return file_ai_proto_rawDescGZIP(), []int{2} } // AIRequest содержит запрос к AI модели @@ -145,7 +210,7 @@ type AIRequest struct { func (x *AIRequest) Reset() { *x = AIRequest{} - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[0] + mi := &file_ai_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -157,7 +222,7 @@ func (x *AIRequest) String() string { func (*AIRequest) ProtoMessage() {} func (x *AIRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[0] + mi := &file_ai_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -170,7 +235,7 @@ func (x *AIRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AIRequest.ProtoReflect.Descriptor instead. func (*AIRequest) Descriptor() ([]byte, []int) { - return file_proto_l_ai_core_proto_ai_proto_rawDescGZIP(), []int{0} + return file_ai_proto_rawDescGZIP(), []int{0} } func (x *AIRequest) GetText() string { @@ -246,7 +311,7 @@ type AnswerGenerationOptions struct { func (x *AnswerGenerationOptions) Reset() { *x = AnswerGenerationOptions{} - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[1] + mi := &file_ai_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -258,7 +323,7 @@ func (x *AnswerGenerationOptions) String() string { func (*AnswerGenerationOptions) ProtoMessage() {} func (x *AnswerGenerationOptions) ProtoReflect() protoreflect.Message { - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[1] + mi := &file_ai_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -271,7 +336,7 @@ func (x *AnswerGenerationOptions) ProtoReflect() protoreflect.Message { // Deprecated: Use AnswerGenerationOptions.ProtoReflect.Descriptor instead. func (*AnswerGenerationOptions) Descriptor() ([]byte, []int) { - return file_proto_l_ai_core_proto_ai_proto_rawDescGZIP(), []int{1} + return file_ai_proto_rawDescGZIP(), []int{1} } func (x *AnswerGenerationOptions) GetIncludeUserMemories() bool { @@ -297,7 +362,7 @@ type AIResponse struct { func (x *AIResponse) Reset() { *x = AIResponse{} - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[2] + mi := &file_ai_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -309,7 +374,7 @@ func (x *AIResponse) String() string { func (*AIResponse) ProtoMessage() {} func (x *AIResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[2] + mi := &file_ai_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -322,7 +387,7 @@ func (x *AIResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AIResponse.ProtoReflect.Descriptor instead. func (*AIResponse) Descriptor() ([]byte, []int) { - return file_proto_l_ai_core_proto_ai_proto_rawDescGZIP(), []int{2} + return file_ai_proto_rawDescGZIP(), []int{2} } func (x *AIResponse) GetContent() string { @@ -377,7 +442,7 @@ type GetAnswerJobRequest struct { func (x *GetAnswerJobRequest) Reset() { *x = GetAnswerJobRequest{} - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[3] + mi := &file_ai_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -389,7 +454,7 @@ func (x *GetAnswerJobRequest) String() string { func (*GetAnswerJobRequest) ProtoMessage() {} func (x *GetAnswerJobRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[3] + mi := &file_ai_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -402,7 +467,7 @@ func (x *GetAnswerJobRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAnswerJobRequest.ProtoReflect.Descriptor instead. func (*GetAnswerJobRequest) Descriptor() ([]byte, []int) { - return file_proto_l_ai_core_proto_ai_proto_rawDescGZIP(), []int{3} + return file_ai_proto_rawDescGZIP(), []int{3} } func (x *GetAnswerJobRequest) GetJobId() string { @@ -425,7 +490,7 @@ type AnswerJobResponse struct { func (x *AnswerJobResponse) Reset() { *x = AnswerJobResponse{} - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[4] + mi := &file_ai_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -437,7 +502,7 @@ func (x *AnswerJobResponse) String() string { func (*AnswerJobResponse) ProtoMessage() {} func (x *AnswerJobResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[4] + mi := &file_ai_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -450,7 +515,7 @@ func (x *AnswerJobResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AnswerJobResponse.ProtoReflect.Descriptor instead. func (*AnswerJobResponse) Descriptor() ([]byte, []int) { - return file_proto_l_ai_core_proto_ai_proto_rawDescGZIP(), []int{4} + return file_ai_proto_rawDescGZIP(), []int{4} } func (x *AnswerJobResponse) GetState() AnswerJobState { @@ -494,7 +559,7 @@ type CreateSessionRequest struct { func (x *CreateSessionRequest) Reset() { *x = CreateSessionRequest{} - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[5] + mi := &file_ai_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -506,7 +571,7 @@ func (x *CreateSessionRequest) String() string { func (*CreateSessionRequest) ProtoMessage() {} func (x *CreateSessionRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[5] + mi := &file_ai_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -519,7 +584,7 @@ func (x *CreateSessionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateSessionRequest.ProtoReflect.Descriptor instead. func (*CreateSessionRequest) Descriptor() ([]byte, []int) { - return file_proto_l_ai_core_proto_ai_proto_rawDescGZIP(), []int{5} + return file_ai_proto_rawDescGZIP(), []int{5} } func (x *CreateSessionRequest) GetUserId() string { @@ -562,7 +627,7 @@ type CreateSessionResponse struct { func (x *CreateSessionResponse) Reset() { *x = CreateSessionResponse{} - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[6] + mi := &file_ai_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -574,7 +639,7 @@ func (x *CreateSessionResponse) String() string { func (*CreateSessionResponse) ProtoMessage() {} func (x *CreateSessionResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[6] + mi := &file_ai_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -587,7 +652,7 @@ func (x *CreateSessionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateSessionResponse.ProtoReflect.Descriptor instead. func (*CreateSessionResponse) Descriptor() ([]byte, []int) { - return file_proto_l_ai_core_proto_ai_proto_rawDescGZIP(), []int{6} + return file_ai_proto_rawDescGZIP(), []int{6} } func (x *CreateSessionResponse) GetSessionId() string { @@ -625,7 +690,7 @@ type AskInSessionRequest struct { func (x *AskInSessionRequest) Reset() { *x = AskInSessionRequest{} - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[7] + mi := &file_ai_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -637,7 +702,7 @@ func (x *AskInSessionRequest) String() string { func (*AskInSessionRequest) ProtoMessage() {} func (x *AskInSessionRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[7] + mi := &file_ai_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -650,7 +715,7 @@ func (x *AskInSessionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AskInSessionRequest.ProtoReflect.Descriptor instead. func (*AskInSessionRequest) Descriptor() ([]byte, []int) { - return file_proto_l_ai_core_proto_ai_proto_rawDescGZIP(), []int{7} + return file_ai_proto_rawDescGZIP(), []int{7} } func (x *AskInSessionRequest) GetSessionId() string { @@ -703,7 +768,7 @@ type SessionStatusResponse struct { func (x *SessionStatusResponse) Reset() { *x = SessionStatusResponse{} - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[8] + mi := &file_ai_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -715,7 +780,7 @@ func (x *SessionStatusResponse) String() string { func (*SessionStatusResponse) ProtoMessage() {} func (x *SessionStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[8] + mi := &file_ai_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -728,7 +793,7 @@ func (x *SessionStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SessionStatusResponse.ProtoReflect.Descriptor instead. func (*SessionStatusResponse) Descriptor() ([]byte, []int) { - return file_proto_l_ai_core_proto_ai_proto_rawDescGZIP(), []int{8} + return file_ai_proto_rawDescGZIP(), []int{8} } func (x *SessionStatusResponse) GetSessionId() string { @@ -784,7 +849,7 @@ type StatelessMessage struct { func (x *StatelessMessage) Reset() { *x = StatelessMessage{} - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[9] + mi := &file_ai_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -796,7 +861,7 @@ func (x *StatelessMessage) String() string { func (*StatelessMessage) ProtoMessage() {} func (x *StatelessMessage) ProtoReflect() protoreflect.Message { - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[9] + mi := &file_ai_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -809,7 +874,7 @@ func (x *StatelessMessage) ProtoReflect() protoreflect.Message { // Deprecated: Use StatelessMessage.ProtoReflect.Descriptor instead. func (*StatelessMessage) Descriptor() ([]byte, []int) { - return file_proto_l_ai_core_proto_ai_proto_rawDescGZIP(), []int{9} + return file_ai_proto_rawDescGZIP(), []int{9} } func (x *StatelessMessage) GetRole() string { @@ -837,7 +902,7 @@ type StatelessRequest struct { func (x *StatelessRequest) Reset() { *x = StatelessRequest{} - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[10] + mi := &file_ai_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -849,7 +914,7 @@ func (x *StatelessRequest) String() string { func (*StatelessRequest) ProtoMessage() {} func (x *StatelessRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[10] + mi := &file_ai_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -862,7 +927,7 @@ func (x *StatelessRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StatelessRequest.ProtoReflect.Descriptor instead. func (*StatelessRequest) Descriptor() ([]byte, []int) { - return file_proto_l_ai_core_proto_ai_proto_rawDescGZIP(), []int{10} + return file_ai_proto_rawDescGZIP(), []int{10} } func (x *StatelessRequest) GetMessages() []*StatelessMessage { @@ -888,7 +953,7 @@ type HealthCheckRequest struct { func (x *HealthCheckRequest) Reset() { *x = HealthCheckRequest{} - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[11] + mi := &file_ai_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -900,7 +965,7 @@ func (x *HealthCheckRequest) String() string { func (*HealthCheckRequest) ProtoMessage() {} func (x *HealthCheckRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[11] + mi := &file_ai_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -913,7 +978,7 @@ func (x *HealthCheckRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use HealthCheckRequest.ProtoReflect.Descriptor instead. func (*HealthCheckRequest) Descriptor() ([]byte, []int) { - return file_proto_l_ai_core_proto_ai_proto_rawDescGZIP(), []int{11} + return file_ai_proto_rawDescGZIP(), []int{11} } // HealthCheckResponse для ответа на проверку здоровья @@ -927,7 +992,7 @@ type HealthCheckResponse struct { func (x *HealthCheckResponse) Reset() { *x = HealthCheckResponse{} - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[12] + mi := &file_ai_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -939,7 +1004,7 @@ func (x *HealthCheckResponse) String() string { func (*HealthCheckResponse) ProtoMessage() {} func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_l_ai_core_proto_ai_proto_msgTypes[12] + mi := &file_ai_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -952,7 +1017,7 @@ func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use HealthCheckResponse.ProtoReflect.Descriptor instead. func (*HealthCheckResponse) Descriptor() ([]byte, []int) { - return file_proto_l_ai_core_proto_ai_proto_rawDescGZIP(), []int{12} + return file_ai_proto_rawDescGZIP(), []int{12} } func (x *HealthCheckResponse) GetServing() bool { @@ -969,11 +1034,559 @@ func (x *HealthCheckResponse) GetVersion() string { return "" } -var File_proto_l_ai_core_proto_ai_proto protoreflect.FileDescriptor +// AgentToolLoopOptions задаёт лимиты tool loop. Значения клиента могут только +// сузить серверные потолки (max_tool_iterations/max_total_tool_calls ≤ server max). +type AgentToolLoopOptions struct { + state protoimpl.MessageState `protogen:"open.v1"` + ToolsEnabled bool `protobuf:"varint,1,opt,name=tools_enabled,json=toolsEnabled,proto3" json:"tools_enabled,omitempty"` // false — модель отвечает без tools + MaxToolIterations uint32 `protobuf:"varint,2,opt,name=max_tool_iterations,json=maxToolIterations,proto3" json:"max_tool_iterations,omitempty"` // потолок round-trip'ов модель↔tools (0 = серверный default) + MaxTotalToolCalls uint32 `protobuf:"varint,3,opt,name=max_total_tool_calls,json=maxTotalToolCalls,proto3" json:"max_total_tool_calls,omitempty"` // потолок РЕАЛЬНЫХ tool call'ов, не итераций (0 = серверный default) + ToolTimeoutSeconds uint32 `protobuf:"varint,4,opt,name=tool_timeout_seconds,json=toolTimeoutSeconds,proto3" json:"tool_timeout_seconds,omitempty"` // таймаут одного tool call (0 = серверный default) + TotalTimeoutSeconds uint32 `protobuf:"varint,5,opt,name=total_timeout_seconds,json=totalTimeoutSeconds,proto3" json:"total_timeout_seconds,omitempty"` // wall-clock таймаут всего вызова (0 = серверный default), независим от budget + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} -const file_proto_l_ai_core_proto_ai_proto_rawDesc = "" + +func (x *AgentToolLoopOptions) Reset() { + *x = AgentToolLoopOptions{} + mi := &file_ai_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AgentToolLoopOptions) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentToolLoopOptions) ProtoMessage() {} + +func (x *AgentToolLoopOptions) ProtoReflect() protoreflect.Message { + mi := &file_ai_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentToolLoopOptions.ProtoReflect.Descriptor instead. +func (*AgentToolLoopOptions) Descriptor() ([]byte, []int) { + return file_ai_proto_rawDescGZIP(), []int{13} +} + +func (x *AgentToolLoopOptions) GetToolsEnabled() bool { + if x != nil { + return x.ToolsEnabled + } + return false +} + +func (x *AgentToolLoopOptions) GetMaxToolIterations() uint32 { + if x != nil { + return x.MaxToolIterations + } + return 0 +} + +func (x *AgentToolLoopOptions) GetMaxTotalToolCalls() uint32 { + if x != nil { + return x.MaxTotalToolCalls + } + return 0 +} + +func (x *AgentToolLoopOptions) GetToolTimeoutSeconds() uint32 { + if x != nil { + return x.ToolTimeoutSeconds + } + return 0 +} + +func (x *AgentToolLoopOptions) GetTotalTimeoutSeconds() uint32 { + if x != nil { + return x.TotalTimeoutSeconds + } + return 0 +} + +// AgentResponseContract описывает требования к структурированному ответу. +type AgentResponseContract struct { + state protoimpl.MessageState `protogen:"open.v1"` + Format AgentOutputFormat `protobuf:"varint,1,opt,name=format,proto3,enum=ai.AgentOutputFormat" json:"format,omitempty"` + JsonSchema string `protobuf:"bytes,2,opt,name=json_schema,json=jsonSchema,proto3" json:"json_schema,omitempty"` // JSON Schema (draft-07), обязателен при format=JSON_SCHEMA + RepairInvalidOutput bool `protobuf:"varint,3,opt,name=repair_invalid_output,json=repairInvalidOutput,proto3" json:"repair_invalid_output,omitempty"` // при провале валидации — один repair-запрос модели (без новых tools) + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AgentResponseContract) Reset() { + *x = AgentResponseContract{} + mi := &file_ai_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AgentResponseContract) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentResponseContract) ProtoMessage() {} + +func (x *AgentResponseContract) ProtoReflect() protoreflect.Message { + mi := &file_ai_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentResponseContract.ProtoReflect.Descriptor instead. +func (*AgentResponseContract) Descriptor() ([]byte, []int) { + return file_ai_proto_rawDescGZIP(), []int{14} +} + +func (x *AgentResponseContract) GetFormat() AgentOutputFormat { + if x != nil { + return x.Format + } + return AgentOutputFormat_AGENT_OUTPUT_FORMAT_UNSPECIFIED +} + +func (x *AgentResponseContract) GetJsonSchema() string { + if x != nil { + return x.JsonSchema + } + return "" +} + +func (x *AgentResponseContract) GetRepairInvalidOutput() bool { + if x != nil { + return x.RepairInvalidOutput + } + return false +} + +// AgentRequest — запрос агентного вызова. +type AgentRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Messages []*StatelessMessage `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` // полный контекст, включая system + ModelName string `protobuf:"bytes,2,opt,name=model_name,json=modelName,proto3" json:"model_name,omitempty"` // модель (по умолчанию deepseek-v4-flash) + EnableReasoning bool `protobuf:"varint,3,opt,name=enable_reasoning,json=enableReasoning,proto3" json:"enable_reasoning,omitempty"` // reasoning не попадает в обычный content + AgentId string `protobuf:"bytes,4,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"` // идентификатор логического агента (для логов/метрик, НЕ авторизация) + ToolProfile string `protobuf:"bytes,5,opt,name=tool_profile,json=toolProfile,proto3" json:"tool_profile,omitempty"` // серверный профиль tools (НЕ авторизация; проверяется против credential) + RequestedTools []string `protobuf:"bytes,6,rep,name=requested_tools,json=requestedTools,proto3" json:"requested_tools,omitempty"` // сужение профиля: effective = profile.allowed ∩ requested; пусто = все allowed + ToolLoop *AgentToolLoopOptions `protobuf:"bytes,7,opt,name=tool_loop,json=toolLoop,proto3" json:"tool_loop,omitempty"` + ResponseContract *AgentResponseContract `protobuf:"bytes,8,opt,name=response_contract,json=responseContract,proto3" json:"response_contract,omitempty"` + RequestId string `protobuf:"bytes,9,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` // сквозной идентификатор для трассировки + IdempotencyKey string `protobuf:"bytes,10,opt,name=idempotency_key,json=idempotencyKey,proto3" json:"idempotency_key,omitempty"` // повторный RunAgent с тем же ключом не создаёт дубликат job + SupportsDeferred bool `protobuf:"varint,11,opt,name=supports_deferred,json=supportsDeferred,proto3" json:"supports_deferred,omitempty"` // клиент умеет status=PENDING + GetAnswerJob для тяжёлых операций + Metadata map[string]string `protobuf:"bytes,12,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` // произвольные метки (не логируются как секреты) + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AgentRequest) Reset() { + *x = AgentRequest{} + mi := &file_ai_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AgentRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentRequest) ProtoMessage() {} + +func (x *AgentRequest) ProtoReflect() protoreflect.Message { + mi := &file_ai_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentRequest.ProtoReflect.Descriptor instead. +func (*AgentRequest) Descriptor() ([]byte, []int) { + return file_ai_proto_rawDescGZIP(), []int{15} +} + +func (x *AgentRequest) GetMessages() []*StatelessMessage { + if x != nil { + return x.Messages + } + return nil +} + +func (x *AgentRequest) GetModelName() string { + if x != nil { + return x.ModelName + } + return "" +} + +func (x *AgentRequest) GetEnableReasoning() bool { + if x != nil { + return x.EnableReasoning + } + return false +} + +func (x *AgentRequest) GetAgentId() string { + if x != nil { + return x.AgentId + } + return "" +} + +func (x *AgentRequest) GetToolProfile() string { + if x != nil { + return x.ToolProfile + } + return "" +} + +func (x *AgentRequest) GetRequestedTools() []string { + if x != nil { + return x.RequestedTools + } + return nil +} + +func (x *AgentRequest) GetToolLoop() *AgentToolLoopOptions { + if x != nil { + return x.ToolLoop + } + return nil +} + +func (x *AgentRequest) GetResponseContract() *AgentResponseContract { + if x != nil { + return x.ResponseContract + } + return nil +} + +func (x *AgentRequest) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +func (x *AgentRequest) GetIdempotencyKey() string { + if x != nil { + return x.IdempotencyKey + } + return "" +} + +func (x *AgentRequest) GetSupportsDeferred() bool { + if x != nil { + return x.SupportsDeferred + } + return false +} + +func (x *AgentRequest) GetMetadata() map[string]string { + if x != nil { + return x.Metadata + } + return nil +} + +// AgentUsage — счётчики использования за вызов. +type AgentUsage struct { + state protoimpl.MessageState `protogen:"open.v1"` + ToolIterations uint32 `protobuf:"varint,1,opt,name=tool_iterations,json=toolIterations,proto3" json:"tool_iterations,omitempty"` // сколько round-trip'ов модель↔tools произошло + TotalToolCalls uint32 `protobuf:"varint,2,opt,name=total_tool_calls,json=totalToolCalls,proto3" json:"total_tool_calls,omitempty"` // сколько РЕАЛЬНЫХ tool call'ов исполнено (3 calls в одной итерации = 3) + ToolsUsed []string `protobuf:"bytes,3,rep,name=tools_used,json=toolsUsed,proto3" json:"tools_used,omitempty"` + InputTokens uint64 `protobuf:"varint,4,opt,name=input_tokens,json=inputTokens,proto3" json:"input_tokens,omitempty"` + OutputTokens uint64 `protobuf:"varint,5,opt,name=output_tokens,json=outputTokens,proto3" json:"output_tokens,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AgentUsage) Reset() { + *x = AgentUsage{} + mi := &file_ai_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AgentUsage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentUsage) ProtoMessage() {} + +func (x *AgentUsage) ProtoReflect() protoreflect.Message { + mi := &file_ai_proto_msgTypes[16] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentUsage.ProtoReflect.Descriptor instead. +func (*AgentUsage) Descriptor() ([]byte, []int) { + return file_ai_proto_rawDescGZIP(), []int{16} +} + +func (x *AgentUsage) GetToolIterations() uint32 { + if x != nil { + return x.ToolIterations + } + return 0 +} + +func (x *AgentUsage) GetTotalToolCalls() uint32 { + if x != nil { + return x.TotalToolCalls + } + return 0 +} + +func (x *AgentUsage) GetToolsUsed() []string { + if x != nil { + return x.ToolsUsed + } + return nil +} + +func (x *AgentUsage) GetInputTokens() uint64 { + if x != nil { + return x.InputTokens + } + return 0 +} + +func (x *AgentUsage) GetOutputTokens() uint64 { + if x != nil { + return x.OutputTokens + } + return 0 +} + +// AgentResponse — результат агентного вызова. +type AgentResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Status AnswerStatus `protobuf:"varint,1,opt,name=status,proto3,enum=ai.AnswerStatus" json:"status,omitempty"` // COMPLETE — content финальный; PENDING — тянуть через GetAnswerJob(job_id) + Content string `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` + Error string `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` + DurationMs float64 `protobuf:"fixed64,4,opt,name=duration_ms,json=durationMs,proto3" json:"duration_ms,omitempty"` + JobId string `protobuf:"bytes,5,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` // при status=PENDING + Usage *AgentUsage `protobuf:"bytes,6,opt,name=usage,proto3" json:"usage,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AgentResponse) Reset() { + *x = AgentResponse{} + mi := &file_ai_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AgentResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentResponse) ProtoMessage() {} + +func (x *AgentResponse) ProtoReflect() protoreflect.Message { + mi := &file_ai_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentResponse.ProtoReflect.Descriptor instead. +func (*AgentResponse) Descriptor() ([]byte, []int) { + return file_ai_proto_rawDescGZIP(), []int{17} +} + +func (x *AgentResponse) GetStatus() AnswerStatus { + if x != nil { + return x.Status + } + return AnswerStatus_ANSWER_STATUS_UNSPECIFIED +} + +func (x *AgentResponse) GetContent() string { + if x != nil { + return x.Content + } + return "" +} + +func (x *AgentResponse) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *AgentResponse) GetDurationMs() float64 { + if x != nil { + return x.DurationMs + } + return 0 +} + +func (x *AgentResponse) GetJobId() string { + if x != nil { + return x.JobId + } + return "" +} + +func (x *AgentResponse) GetUsage() *AgentUsage { + if x != nil { + return x.Usage + } + return nil +} + +// CancelAnswerJobRequest — запрос отмены отложенной job (идемпотентный). +type CancelAnswerJobRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + JobId string `protobuf:"bytes,1,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` + Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` // причина отмены (сохраняется), опционально + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CancelAnswerJobRequest) Reset() { + *x = CancelAnswerJobRequest{} + mi := &file_ai_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CancelAnswerJobRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CancelAnswerJobRequest) ProtoMessage() {} + +func (x *CancelAnswerJobRequest) ProtoReflect() protoreflect.Message { + mi := &file_ai_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CancelAnswerJobRequest.ProtoReflect.Descriptor instead. +func (*CancelAnswerJobRequest) Descriptor() ([]byte, []int) { + return file_ai_proto_rawDescGZIP(), []int{18} +} + +func (x *CancelAnswerJobRequest) GetJobId() string { + if x != nil { + return x.JobId + } + return "" +} + +func (x *CancelAnswerJobRequest) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +// CancelAnswerJobResponse — результат отмены. +type CancelAnswerJobResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + State AnswerJobState `protobuf:"varint,1,opt,name=state,proto3,enum=ai.AnswerJobState" json:"state,omitempty"` // состояние job после запроса (CANCELLED, либо терминальное, если уже завершилась) + Cancelled bool `protobuf:"varint,2,opt,name=cancelled,proto3" json:"cancelled,omitempty"` // true — отмена применена этим вызовом; false — job уже была терминальна/отменена + Error string `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CancelAnswerJobResponse) Reset() { + *x = CancelAnswerJobResponse{} + mi := &file_ai_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CancelAnswerJobResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CancelAnswerJobResponse) ProtoMessage() {} + +func (x *CancelAnswerJobResponse) ProtoReflect() protoreflect.Message { + mi := &file_ai_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CancelAnswerJobResponse.ProtoReflect.Descriptor instead. +func (*CancelAnswerJobResponse) Descriptor() ([]byte, []int) { + return file_ai_proto_rawDescGZIP(), []int{19} +} + +func (x *CancelAnswerJobResponse) GetState() AnswerJobState { + if x != nil { + return x.State + } + return AnswerJobState_ANSWER_JOB_STATE_UNSPECIFIED +} + +func (x *CancelAnswerJobResponse) GetCancelled() bool { + if x != nil { + return x.Cancelled + } + return false +} + +func (x *CancelAnswerJobResponse) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +var File_ai_proto protoreflect.FileDescriptor + +const file_ai_proto_rawDesc = "" + "\n" + - "\x1eproto/l-ai-core-proto/ai.proto\x12\x02ai\"\xe4\x02\n" + + "\bai.proto\x12\x02ai\"\xe4\x02\n" + "\tAIRequest\x12\x12\n" + "\x04text\x18\x01 \x01(\tR\x04text\x12!\n" + "\fbot_username\x18\x02 \x01(\tR\vbotUsername\x12\x1d\n" + @@ -1042,18 +1655,81 @@ const file_proto_l_ai_core_proto_ai_proto_rawDesc = "" + "\x12HealthCheckRequest\"I\n" + "\x13HealthCheckResponse\x12\x18\n" + "\aserving\x18\x01 \x01(\bR\aserving\x12\x18\n" + - "\aversion\x18\x02 \x01(\tR\aversion*d\n" + + "\aversion\x18\x02 \x01(\tR\aversion\"\x82\x02\n" + + "\x14AgentToolLoopOptions\x12#\n" + + "\rtools_enabled\x18\x01 \x01(\bR\ftoolsEnabled\x12.\n" + + "\x13max_tool_iterations\x18\x02 \x01(\rR\x11maxToolIterations\x12/\n" + + "\x14max_total_tool_calls\x18\x03 \x01(\rR\x11maxTotalToolCalls\x120\n" + + "\x14tool_timeout_seconds\x18\x04 \x01(\rR\x12toolTimeoutSeconds\x122\n" + + "\x15total_timeout_seconds\x18\x05 \x01(\rR\x13totalTimeoutSeconds\"\x9b\x01\n" + + "\x15AgentResponseContract\x12-\n" + + "\x06format\x18\x01 \x01(\x0e2\x15.ai.AgentOutputFormatR\x06format\x12\x1f\n" + + "\vjson_schema\x18\x02 \x01(\tR\n" + + "jsonSchema\x122\n" + + "\x15repair_invalid_output\x18\x03 \x01(\bR\x13repairInvalidOutput\"\xde\x04\n" + + "\fAgentRequest\x120\n" + + "\bmessages\x18\x01 \x03(\v2\x14.ai.StatelessMessageR\bmessages\x12\x1d\n" + + "\n" + + "model_name\x18\x02 \x01(\tR\tmodelName\x12)\n" + + "\x10enable_reasoning\x18\x03 \x01(\bR\x0fenableReasoning\x12\x19\n" + + "\bagent_id\x18\x04 \x01(\tR\aagentId\x12!\n" + + "\ftool_profile\x18\x05 \x01(\tR\vtoolProfile\x12'\n" + + "\x0frequested_tools\x18\x06 \x03(\tR\x0erequestedTools\x125\n" + + "\ttool_loop\x18\a \x01(\v2\x18.ai.AgentToolLoopOptionsR\btoolLoop\x12F\n" + + "\x11response_contract\x18\b \x01(\v2\x19.ai.AgentResponseContractR\x10responseContract\x12\x1d\n" + + "\n" + + "request_id\x18\t \x01(\tR\trequestId\x12'\n" + + "\x0fidempotency_key\x18\n" + + " \x01(\tR\x0eidempotencyKey\x12+\n" + + "\x11supports_deferred\x18\v \x01(\bR\x10supportsDeferred\x12:\n" + + "\bmetadata\x18\f \x03(\v2\x1e.ai.AgentRequest.MetadataEntryR\bmetadata\x1a;\n" + + "\rMetadataEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xc6\x01\n" + + "\n" + + "AgentUsage\x12'\n" + + "\x0ftool_iterations\x18\x01 \x01(\rR\x0etoolIterations\x12(\n" + + "\x10total_tool_calls\x18\x02 \x01(\rR\x0etotalToolCalls\x12\x1d\n" + + "\n" + + "tools_used\x18\x03 \x03(\tR\ttoolsUsed\x12!\n" + + "\finput_tokens\x18\x04 \x01(\x04R\vinputTokens\x12#\n" + + "\routput_tokens\x18\x05 \x01(\x04R\foutputTokens\"\xc7\x01\n" + + "\rAgentResponse\x12(\n" + + "\x06status\x18\x01 \x01(\x0e2\x10.ai.AnswerStatusR\x06status\x12\x18\n" + + "\acontent\x18\x02 \x01(\tR\acontent\x12\x14\n" + + "\x05error\x18\x03 \x01(\tR\x05error\x12\x1f\n" + + "\vduration_ms\x18\x04 \x01(\x01R\n" + + "durationMs\x12\x15\n" + + "\x06job_id\x18\x05 \x01(\tR\x05jobId\x12$\n" + + "\x05usage\x18\x06 \x01(\v2\x0e.ai.AgentUsageR\x05usage\"G\n" + + "\x16CancelAnswerJobRequest\x12\x15\n" + + "\x06job_id\x18\x01 \x01(\tR\x05jobId\x12\x16\n" + + "\x06reason\x18\x02 \x01(\tR\x06reason\"w\n" + + "\x17CancelAnswerJobResponse\x12(\n" + + "\x05state\x18\x01 \x01(\x0e2\x12.ai.AnswerJobStateR\x05state\x12\x1c\n" + + "\tcancelled\x18\x02 \x01(\bR\tcancelled\x12\x14\n" + + "\x05error\x18\x03 \x01(\tR\x05error*d\n" + "\fAnswerStatus\x12\x1d\n" + "\x19ANSWER_STATUS_UNSPECIFIED\x10\x00\x12\x1a\n" + "\x16ANSWER_STATUS_COMPLETE\x10\x01\x12\x19\n" + - "\x15ANSWER_STATUS_PENDING\x10\x02*\xa8\x01\n" + + "\x15ANSWER_STATUS_PENDING\x10\x02*\x84\x02\n" + "\x0eAnswerJobState\x12 \n" + "\x1cANSWER_JOB_STATE_UNSPECIFIED\x10\x00\x12\x1c\n" + "\x18ANSWER_JOB_STATE_PENDING\x10\x01\x12\x19\n" + "\x15ANSWER_JOB_STATE_DONE\x10\x02\x12\x1b\n" + "\x17ANSWER_JOB_STATE_FAILED\x10\x03\x12\x1e\n" + - "\x1aANSWER_JOB_STATE_NOT_FOUND\x10\x042\x81\x03\n" + + "\x1aANSWER_JOB_STATE_NOT_FOUND\x10\x04\x12\x1c\n" + + "\x18ANSWER_JOB_STATE_RUNNING\x10\x05\x12\x1e\n" + + "\x1aANSWER_JOB_STATE_CANCELLED\x10\x06\x12\x1c\n" + + "\x18ANSWER_JOB_STATE_EXPIRED\x10\a*\x99\x01\n" + + "\x11AgentOutputFormat\x12#\n" + + "\x1fAGENT_OUTPUT_FORMAT_UNSPECIFIED\x10\x00\x12\x1c\n" + + "\x18AGENT_OUTPUT_FORMAT_TEXT\x10\x01\x12\x1c\n" + + "\x18AGENT_OUTPUT_FORMAT_JSON\x10\x02\x12#\n" + + "\x1fAGENT_OUTPUT_FORMAT_JSON_SCHEMA\x10\x032\xfe\x03\n" + "\tAIService\x12/\n" + + "\bRunAgent\x12\x10.ai.AgentRequest\x1a\x11.ai.AgentResponse\x12J\n" + + "\x0fCancelAnswerJob\x12\x1a.ai.CancelAnswerJobRequest\x1a\x1b.ai.CancelAnswerJobResponse\x12/\n" + "\x0eGenerateAnswer\x12\r.ai.AIRequest\x1a\x0e.ai.AIResponse\x129\n" + "\x11GenerateStateless\x12\x14.ai.StatelessRequest\x1a\x0e.ai.AIResponse\x12>\n" + "\vHealthCheck\x12\x16.ai.HealthCheckRequest\x1a\x17.ai.HealthCheckResponse\x12D\n" + @@ -1062,82 +1738,103 @@ const file_proto_l_ai_core_proto_ai_proto_rawDesc = "" + "\fGetAnswerJob\x12\x17.ai.GetAnswerJobRequest\x1a\x15.ai.AnswerJobResponseB,Z*git.be.kaien.ru/l-ai/l-ai-core-proto/protob\x06proto3" var ( - file_proto_l_ai_core_proto_ai_proto_rawDescOnce sync.Once - file_proto_l_ai_core_proto_ai_proto_rawDescData []byte + file_ai_proto_rawDescOnce sync.Once + file_ai_proto_rawDescData []byte ) -func file_proto_l_ai_core_proto_ai_proto_rawDescGZIP() []byte { - file_proto_l_ai_core_proto_ai_proto_rawDescOnce.Do(func() { - file_proto_l_ai_core_proto_ai_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proto_l_ai_core_proto_ai_proto_rawDesc), len(file_proto_l_ai_core_proto_ai_proto_rawDesc))) +func file_ai_proto_rawDescGZIP() []byte { + file_ai_proto_rawDescOnce.Do(func() { + file_ai_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ai_proto_rawDesc), len(file_ai_proto_rawDesc))) }) - return file_proto_l_ai_core_proto_ai_proto_rawDescData + return file_ai_proto_rawDescData } -var file_proto_l_ai_core_proto_ai_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_proto_l_ai_core_proto_ai_proto_msgTypes = make([]protoimpl.MessageInfo, 13) -var file_proto_l_ai_core_proto_ai_proto_goTypes = []any{ +var file_ai_proto_enumTypes = make([]protoimpl.EnumInfo, 3) +var file_ai_proto_msgTypes = make([]protoimpl.MessageInfo, 21) +var file_ai_proto_goTypes = []any{ (AnswerStatus)(0), // 0: ai.AnswerStatus (AnswerJobState)(0), // 1: ai.AnswerJobState - (*AIRequest)(nil), // 2: ai.AIRequest - (*AnswerGenerationOptions)(nil), // 3: ai.AnswerGenerationOptions - (*AIResponse)(nil), // 4: ai.AIResponse - (*GetAnswerJobRequest)(nil), // 5: ai.GetAnswerJobRequest - (*AnswerJobResponse)(nil), // 6: ai.AnswerJobResponse - (*CreateSessionRequest)(nil), // 7: ai.CreateSessionRequest - (*CreateSessionResponse)(nil), // 8: ai.CreateSessionResponse - (*AskInSessionRequest)(nil), // 9: ai.AskInSessionRequest - (*SessionStatusResponse)(nil), // 10: ai.SessionStatusResponse - (*StatelessMessage)(nil), // 11: ai.StatelessMessage - (*StatelessRequest)(nil), // 12: ai.StatelessRequest - (*HealthCheckRequest)(nil), // 13: ai.HealthCheckRequest - (*HealthCheckResponse)(nil), // 14: ai.HealthCheckResponse + (AgentOutputFormat)(0), // 2: ai.AgentOutputFormat + (*AIRequest)(nil), // 3: ai.AIRequest + (*AnswerGenerationOptions)(nil), // 4: ai.AnswerGenerationOptions + (*AIResponse)(nil), // 5: ai.AIResponse + (*GetAnswerJobRequest)(nil), // 6: ai.GetAnswerJobRequest + (*AnswerJobResponse)(nil), // 7: ai.AnswerJobResponse + (*CreateSessionRequest)(nil), // 8: ai.CreateSessionRequest + (*CreateSessionResponse)(nil), // 9: ai.CreateSessionResponse + (*AskInSessionRequest)(nil), // 10: ai.AskInSessionRequest + (*SessionStatusResponse)(nil), // 11: ai.SessionStatusResponse + (*StatelessMessage)(nil), // 12: ai.StatelessMessage + (*StatelessRequest)(nil), // 13: ai.StatelessRequest + (*HealthCheckRequest)(nil), // 14: ai.HealthCheckRequest + (*HealthCheckResponse)(nil), // 15: ai.HealthCheckResponse + (*AgentToolLoopOptions)(nil), // 16: ai.AgentToolLoopOptions + (*AgentResponseContract)(nil), // 17: ai.AgentResponseContract + (*AgentRequest)(nil), // 18: ai.AgentRequest + (*AgentUsage)(nil), // 19: ai.AgentUsage + (*AgentResponse)(nil), // 20: ai.AgentResponse + (*CancelAnswerJobRequest)(nil), // 21: ai.CancelAnswerJobRequest + (*CancelAnswerJobResponse)(nil), // 22: ai.CancelAnswerJobResponse + nil, // 23: ai.AgentRequest.MetadataEntry } -var file_proto_l_ai_core_proto_ai_proto_depIdxs = []int32{ - 3, // 0: ai.AIRequest.options:type_name -> ai.AnswerGenerationOptions +var file_ai_proto_depIdxs = []int32{ + 4, // 0: ai.AIRequest.options:type_name -> ai.AnswerGenerationOptions 0, // 1: ai.AIResponse.status:type_name -> ai.AnswerStatus 1, // 2: ai.AnswerJobResponse.state:type_name -> ai.AnswerJobState - 3, // 3: ai.AskInSessionRequest.options:type_name -> ai.AnswerGenerationOptions - 11, // 4: ai.StatelessRequest.messages:type_name -> ai.StatelessMessage - 2, // 5: ai.AIService.GenerateAnswer:input_type -> ai.AIRequest - 12, // 6: ai.AIService.GenerateStateless:input_type -> ai.StatelessRequest - 13, // 7: ai.AIService.HealthCheck:input_type -> ai.HealthCheckRequest - 7, // 8: ai.AIService.CreateSession:input_type -> ai.CreateSessionRequest - 9, // 9: ai.AIService.AskInSession:input_type -> ai.AskInSessionRequest - 5, // 10: ai.AIService.GetAnswerJob:input_type -> ai.GetAnswerJobRequest - 4, // 11: ai.AIService.GenerateAnswer:output_type -> ai.AIResponse - 4, // 12: ai.AIService.GenerateStateless:output_type -> ai.AIResponse - 14, // 13: ai.AIService.HealthCheck:output_type -> ai.HealthCheckResponse - 8, // 14: ai.AIService.CreateSession:output_type -> ai.CreateSessionResponse - 10, // 15: ai.AIService.AskInSession:output_type -> ai.SessionStatusResponse - 6, // 16: ai.AIService.GetAnswerJob:output_type -> ai.AnswerJobResponse - 11, // [11:17] is the sub-list for method output_type - 5, // [5:11] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 4, // 3: ai.AskInSessionRequest.options:type_name -> ai.AnswerGenerationOptions + 12, // 4: ai.StatelessRequest.messages:type_name -> ai.StatelessMessage + 2, // 5: ai.AgentResponseContract.format:type_name -> ai.AgentOutputFormat + 12, // 6: ai.AgentRequest.messages:type_name -> ai.StatelessMessage + 16, // 7: ai.AgentRequest.tool_loop:type_name -> ai.AgentToolLoopOptions + 17, // 8: ai.AgentRequest.response_contract:type_name -> ai.AgentResponseContract + 23, // 9: ai.AgentRequest.metadata:type_name -> ai.AgentRequest.MetadataEntry + 0, // 10: ai.AgentResponse.status:type_name -> ai.AnswerStatus + 19, // 11: ai.AgentResponse.usage:type_name -> ai.AgentUsage + 1, // 12: ai.CancelAnswerJobResponse.state:type_name -> ai.AnswerJobState + 18, // 13: ai.AIService.RunAgent:input_type -> ai.AgentRequest + 21, // 14: ai.AIService.CancelAnswerJob:input_type -> ai.CancelAnswerJobRequest + 3, // 15: ai.AIService.GenerateAnswer:input_type -> ai.AIRequest + 13, // 16: ai.AIService.GenerateStateless:input_type -> ai.StatelessRequest + 14, // 17: ai.AIService.HealthCheck:input_type -> ai.HealthCheckRequest + 8, // 18: ai.AIService.CreateSession:input_type -> ai.CreateSessionRequest + 10, // 19: ai.AIService.AskInSession:input_type -> ai.AskInSessionRequest + 6, // 20: ai.AIService.GetAnswerJob:input_type -> ai.GetAnswerJobRequest + 20, // 21: ai.AIService.RunAgent:output_type -> ai.AgentResponse + 22, // 22: ai.AIService.CancelAnswerJob:output_type -> ai.CancelAnswerJobResponse + 5, // 23: ai.AIService.GenerateAnswer:output_type -> ai.AIResponse + 5, // 24: ai.AIService.GenerateStateless:output_type -> ai.AIResponse + 15, // 25: ai.AIService.HealthCheck:output_type -> ai.HealthCheckResponse + 9, // 26: ai.AIService.CreateSession:output_type -> ai.CreateSessionResponse + 11, // 27: ai.AIService.AskInSession:output_type -> ai.SessionStatusResponse + 7, // 28: ai.AIService.GetAnswerJob:output_type -> ai.AnswerJobResponse + 21, // [21:29] is the sub-list for method output_type + 13, // [13:21] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name } -func init() { file_proto_l_ai_core_proto_ai_proto_init() } -func file_proto_l_ai_core_proto_ai_proto_init() { - if File_proto_l_ai_core_proto_ai_proto != nil { +func init() { file_ai_proto_init() } +func file_ai_proto_init() { + if File_ai_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_l_ai_core_proto_ai_proto_rawDesc), len(file_proto_l_ai_core_proto_ai_proto_rawDesc)), - NumEnums: 2, - NumMessages: 13, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_ai_proto_rawDesc), len(file_ai_proto_rawDesc)), + NumEnums: 3, + NumMessages: 21, NumExtensions: 0, NumServices: 1, }, - GoTypes: file_proto_l_ai_core_proto_ai_proto_goTypes, - DependencyIndexes: file_proto_l_ai_core_proto_ai_proto_depIdxs, - EnumInfos: file_proto_l_ai_core_proto_ai_proto_enumTypes, - MessageInfos: file_proto_l_ai_core_proto_ai_proto_msgTypes, + GoTypes: file_ai_proto_goTypes, + DependencyIndexes: file_ai_proto_depIdxs, + EnumInfos: file_ai_proto_enumTypes, + MessageInfos: file_ai_proto_msgTypes, }.Build() - File_proto_l_ai_core_proto_ai_proto = out.File - file_proto_l_ai_core_proto_ai_proto_goTypes = nil - file_proto_l_ai_core_proto_ai_proto_depIdxs = nil + File_ai_proto = out.File + file_ai_proto_goTypes = nil + file_ai_proto_depIdxs = nil } diff --git a/ai.proto b/ai.proto index 47c8fd1..263cd3f 100644 --- a/ai.proto +++ b/ai.proto @@ -46,12 +46,18 @@ message GetAnswerJobRequest { } // 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_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 — результат опроса отложенной генерации. @@ -117,8 +123,101 @@ message HealthCheckResponse { 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 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); diff --git a/ai_grpc.pb.go b/ai_grpc.pb.go index 6bcee3b..f139501 100644 --- a/ai_grpc.pb.go +++ b/ai_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.6.1 // - protoc v7.34.0 -// source: proto/l-ai-core-proto/ai.proto +// source: ai.proto package proto @@ -19,6 +19,8 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( + AIService_RunAgent_FullMethodName = "/ai.AIService/RunAgent" + AIService_CancelAnswerJob_FullMethodName = "/ai.AIService/CancelAnswerJob" AIService_GenerateAnswer_FullMethodName = "/ai.AIService/GenerateAnswer" AIService_GenerateStateless_FullMethodName = "/ai.AIService/GenerateStateless" AIService_HealthCheck_FullMethodName = "/ai.AIService/HealthCheck" @@ -33,6 +35,11 @@ const ( // // AIService - основной сервис для работы с AI type AIServiceClient interface { + // RunAgent — надёжный агентный вызов с серверной фильтрацией tools и deferred + // jobs. Требует service-to-service авторизации. + RunAgent(ctx context.Context, in *AgentRequest, opts ...grpc.CallOption) (*AgentResponse, error) + // CancelAnswerJob отменяет отложенную job (идемпотентно). Требует авторизации. + CancelAnswerJob(ctx context.Context, in *CancelAnswerJobRequest, opts ...grpc.CallOption) (*CancelAnswerJobResponse, error) // GenerateAnswer генерирует ответ на запрос GenerateAnswer(ctx context.Context, in *AIRequest, opts ...grpc.CallOption) (*AIResponse, error) // GenerateStateless генерирует ответ из массива messages (system+history+user) @@ -56,6 +63,26 @@ func NewAIServiceClient(cc grpc.ClientConnInterface) AIServiceClient { return &aIServiceClient{cc} } +func (c *aIServiceClient) RunAgent(ctx context.Context, in *AgentRequest, opts ...grpc.CallOption) (*AgentResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(AgentResponse) + err := c.cc.Invoke(ctx, AIService_RunAgent_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *aIServiceClient) CancelAnswerJob(ctx context.Context, in *CancelAnswerJobRequest, opts ...grpc.CallOption) (*CancelAnswerJobResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CancelAnswerJobResponse) + err := c.cc.Invoke(ctx, AIService_CancelAnswerJob_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *aIServiceClient) GenerateAnswer(ctx context.Context, in *AIRequest, opts ...grpc.CallOption) (*AIResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AIResponse) @@ -122,6 +149,11 @@ func (c *aIServiceClient) GetAnswerJob(ctx context.Context, in *GetAnswerJobRequ // // AIService - основной сервис для работы с AI type AIServiceServer interface { + // RunAgent — надёжный агентный вызов с серверной фильтрацией tools и deferred + // jobs. Требует service-to-service авторизации. + RunAgent(context.Context, *AgentRequest) (*AgentResponse, error) + // CancelAnswerJob отменяет отложенную job (идемпотентно). Требует авторизации. + CancelAnswerJob(context.Context, *CancelAnswerJobRequest) (*CancelAnswerJobResponse, error) // GenerateAnswer генерирует ответ на запрос GenerateAnswer(context.Context, *AIRequest) (*AIResponse, error) // GenerateStateless генерирует ответ из массива messages (system+history+user) @@ -145,6 +177,12 @@ type AIServiceServer interface { // pointer dereference when methods are called. type UnimplementedAIServiceServer struct{} +func (UnimplementedAIServiceServer) RunAgent(context.Context, *AgentRequest) (*AgentResponse, error) { + return nil, status.Error(codes.Unimplemented, "method RunAgent not implemented") +} +func (UnimplementedAIServiceServer) CancelAnswerJob(context.Context, *CancelAnswerJobRequest) (*CancelAnswerJobResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CancelAnswerJob not implemented") +} func (UnimplementedAIServiceServer) GenerateAnswer(context.Context, *AIRequest) (*AIResponse, error) { return nil, status.Error(codes.Unimplemented, "method GenerateAnswer not implemented") } @@ -184,6 +222,42 @@ func RegisterAIServiceServer(s grpc.ServiceRegistrar, srv AIServiceServer) { s.RegisterService(&AIService_ServiceDesc, srv) } +func _AIService_RunAgent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AgentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AIServiceServer).RunAgent(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AIService_RunAgent_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AIServiceServer).RunAgent(ctx, req.(*AgentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AIService_CancelAnswerJob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CancelAnswerJobRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AIServiceServer).CancelAnswerJob(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AIService_CancelAnswerJob_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AIServiceServer).CancelAnswerJob(ctx, req.(*CancelAnswerJobRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _AIService_GenerateAnswer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(AIRequest) if err := dec(in); err != nil { @@ -299,6 +373,14 @@ var AIService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "ai.AIService", HandlerType: (*AIServiceServer)(nil), Methods: []grpc.MethodDesc{ + { + MethodName: "RunAgent", + Handler: _AIService_RunAgent_Handler, + }, + { + MethodName: "CancelAnswerJob", + Handler: _AIService_CancelAnswerJob_Handler, + }, { MethodName: "GenerateAnswer", Handler: _AIService_GenerateAnswer_Handler, @@ -325,5 +407,5 @@ var AIService_ServiceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "proto/l-ai-core-proto/ai.proto", + Metadata: "ai.proto", }