syntax = "proto3";

package apf.ai.v1;

import "common.proto";
import "google/protobuf/struct.proto";

option csharp_namespace = "Apf.Ai.Grpc.V1";

// Streaming chat. Server returns a stream of ChatEvent; one event per
// model delta, citation, agent step, tool-call request, error, or done.
// Conversation state is NOT persisted — the caller sends the full
// message history every call. `conversation_id` exists only for audit
// correlation.
service ChatService {
  rpc Chat(ChatRequest) returns (stream ChatEvent);
}

message RagOptions {
  bool enabled = 1;
  int32 top_k = 2; // 0 = use server default
}

message ChatRequest {
  repeated ChatMessage messages = 1;
  string conversation_id = 2;
  string model = 3;
  string provider = 4;
  repeated ToolDescriptor tools_available = 5;
  RagOptions rag = 6;
  // Optional for JWT auth (overridden by JWT claims). Required for
  // API-key auth — but API-key callers should use the HTTP surface.
  Principal principal = 7;
}

message TokenEvent {
  string token = 1;
  string value = 2;
}

message CitationEvent {
  string chunk_id = 1;
  string document_id = 2;
  string source = 3;
  double score = 4;
  string snippet = 5;
}

message AgentStepEvent {
  string agent = 1;
  string step = 2;
  string step_id = 3;
}

message ToolCallEvent {
  string call_id = 1;
  string name = 2;
  google.protobuf.Struct args = 3;
}

message ErrorEvent {
  string code = 1;
  string message = 2;
  bool retriable = 3;
}

message DoneStats {
  int32 tokens_in = 1;
  int32 tokens_out = 2;
  int32 chunks_retrieved = 3;
}

message DoneEvent {
  DoneStats stats = 1;
}

// Polymorphic stream payload. Mirrors the SSE shapes from the OpenAPI
// surface (kept side-by-side for third-party callers). gRPC's `oneof`
// gives us a tagged union without the SSE JSON discriminator.
message ChatEvent {
  oneof event {
    TokenEvent token = 1;
    CitationEvent citation = 2;
    AgentStepEvent agent_step = 3;
    ToolCallEvent tool_call = 4;
    ErrorEvent error = 5;
    DoneEvent done = 6;
  }
}
