feat(proto): AnswerJobResponse.agent_usage (field 5) for deferred RunAgent usage

Additive field so GetAnswerJob can return the same authoritative AgentUsage as
synchronous RunAgent for a completed deferred job. Unset for legacy
GenerateAnswer jobs. No existing field numbers or enum values changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Kaien 2026-08-07 16:37:23 +03:00
parent 00e8f9b190
commit 68d40d95fc
2 changed files with 56 additions and 38 deletions

View File

@ -484,6 +484,10 @@ type AnswerJobResponse struct {
Content string `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` // финальный ответ, когда DONE
Error string `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` // причина, когда FAILED
DurationMs float64 `protobuf:"fixed64,4,opt,name=duration_ms,json=durationMs,proto3" json:"duration_ms,omitempty"` // суммарное время генерации, когда DONE
// Authoritative usage завершённой deferred RunAgent job — тот же счётчик, что
// возвращает synchronous RunAgent (реальные iterations/tool calls core, не
// self-reported из ответа модели). Для legacy GenerateAnswer jobs не заполняется.
AgentUsage *AgentUsage `protobuf:"bytes,5,opt,name=agent_usage,json=agentUsage,proto3" json:"agent_usage,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -546,6 +550,13 @@ func (x *AnswerJobResponse) GetDurationMs() float64 {
return 0
}
func (x *AnswerJobResponse) GetAgentUsage() *AgentUsage {
if x != nil {
return x.AgentUsage
}
return nil
}
// CreateSessionRequest для создания новой AI сессии
type CreateSessionRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
@ -1610,13 +1621,15 @@ const file_ai_proto_rawDesc = "" +
"\x06status\x18\x05 \x01(\x0e2\x10.ai.AnswerStatusR\x06status\x12\x15\n" +
"\x06job_id\x18\x06 \x01(\tR\x05jobId\",\n" +
"\x13GetAnswerJobRequest\x12\x15\n" +
"\x06job_id\x18\x01 \x01(\tR\x05jobId\"\x8e\x01\n" +
"\x06job_id\x18\x01 \x01(\tR\x05jobId\"\xbf\x01\n" +
"\x11AnswerJobResponse\x12(\n" +
"\x05state\x18\x01 \x01(\x0e2\x12.ai.AnswerJobStateR\x05state\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\"\x92\x01\n" +
"durationMs\x12/\n" +
"\vagent_usage\x18\x05 \x01(\v2\x0e.ai.AgentUsageR\n" +
"agentUsage\"\x92\x01\n" +
"\x14CreateSessionRequest\x12\x17\n" +
"\auser_id\x18\x01 \x01(\tR\x06userId\x12!\n" +
"\fbot_username\x18\x02 \x01(\tR\vbotUsername\x12\x1d\n" +
@ -1781,37 +1794,38 @@ 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
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
19, // 3: ai.AnswerJobResponse.agent_usage:type_name -> ai.AgentUsage
4, // 4: ai.AskInSessionRequest.options:type_name -> ai.AnswerGenerationOptions
12, // 5: ai.StatelessRequest.messages:type_name -> ai.StatelessMessage
2, // 6: ai.AgentResponseContract.format:type_name -> ai.AgentOutputFormat
12, // 7: ai.AgentRequest.messages:type_name -> ai.StatelessMessage
16, // 8: ai.AgentRequest.tool_loop:type_name -> ai.AgentToolLoopOptions
17, // 9: ai.AgentRequest.response_contract:type_name -> ai.AgentResponseContract
23, // 10: ai.AgentRequest.metadata:type_name -> ai.AgentRequest.MetadataEntry
0, // 11: ai.AgentResponse.status:type_name -> ai.AnswerStatus
19, // 12: ai.AgentResponse.usage:type_name -> ai.AgentUsage
1, // 13: ai.CancelAnswerJobResponse.state:type_name -> ai.AnswerJobState
18, // 14: ai.AIService.RunAgent:input_type -> ai.AgentRequest
21, // 15: ai.AIService.CancelAnswerJob:input_type -> ai.CancelAnswerJobRequest
3, // 16: ai.AIService.GenerateAnswer:input_type -> ai.AIRequest
13, // 17: ai.AIService.GenerateStateless:input_type -> ai.StatelessRequest
14, // 18: ai.AIService.HealthCheck:input_type -> ai.HealthCheckRequest
8, // 19: ai.AIService.CreateSession:input_type -> ai.CreateSessionRequest
10, // 20: ai.AIService.AskInSession:input_type -> ai.AskInSessionRequest
6, // 21: ai.AIService.GetAnswerJob:input_type -> ai.GetAnswerJobRequest
20, // 22: ai.AIService.RunAgent:output_type -> ai.AgentResponse
22, // 23: ai.AIService.CancelAnswerJob:output_type -> ai.CancelAnswerJobResponse
5, // 24: ai.AIService.GenerateAnswer:output_type -> ai.AIResponse
5, // 25: ai.AIService.GenerateStateless:output_type -> ai.AIResponse
15, // 26: ai.AIService.HealthCheck:output_type -> ai.HealthCheckResponse
9, // 27: ai.AIService.CreateSession:output_type -> ai.CreateSessionResponse
11, // 28: ai.AIService.AskInSession:output_type -> ai.SessionStatusResponse
7, // 29: ai.AIService.GetAnswerJob:output_type -> ai.AnswerJobResponse
22, // [22:30] is the sub-list for method output_type
14, // [14:22] is the sub-list for method input_type
14, // [14:14] is the sub-list for extension type_name
14, // [14:14] is the sub-list for extension extendee
0, // [0:14] is the sub-list for field type_name
}
func init() { file_ai_proto_init() }

View File

@ -66,6 +66,10 @@ message AnswerJobResponse {
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;
}
// CreateSessionRequest для создания новой AI сессии