package inference import ( "context" "crypto/tls" "fmt" "strings" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/metadata" corepb "l-ai-agent-kaya/proto/l-ai-core-proto" ) type CoreRunner struct { api corepb.AIServiceClient token string conn *grpc.ClientConn } func DialCore(target, token string, insecureTransport bool) (*CoreRunner, error) { var tc credentials.TransportCredentials if insecureTransport { tc = insecure.NewCredentials() } else { tc = credentials.NewTLS(&tls.Config{MinVersion: tls.VersionTLS12}) } conn, err := grpc.NewClient(target, grpc.WithTransportCredentials(tc)) if err != nil { return nil, err } return &CoreRunner{api: corepb.NewAIServiceClient(conn), token: token, conn: conn}, nil } func (r *CoreRunner) Close() error { if r.conn != nil { return r.conn.Close() } return nil } func (r *CoreRunner) auth(ctx context.Context) context.Context { if strings.TrimSpace(r.token) == "" { return ctx } return metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+r.token) } func (r *CoreRunner) RunAgent(ctx context.Context, req RunRequest) (RunResult, error) { msgs := make([]*corepb.StatelessMessage, 0, len(req.Messages)) for _, m := range req.Messages { msgs = append(msgs, &corepb.StatelessMessage{Role: m.Role, Content: m.Content}) } format := corepb.AgentOutputFormat_AGENT_OUTPUT_FORMAT_TEXT if req.JSONSchema != "" { format = corepb.AgentOutputFormat_AGENT_OUTPUT_FORMAT_JSON_SCHEMA } toolsEnabled := req.ToolsEnabled maxIters := req.MaxToolIterations maxCalls := req.MaxTotalToolCalls if !toolsEnabled { maxIters = 1 maxCalls = 0 } else { if maxIters == 0 { maxIters = 12 } if maxCalls == 0 { maxCalls = 20 } } resp, err := r.api.RunAgent(r.auth(ctx), &corepb.AgentRequest{ Messages: msgs, ModelName: req.Model, AgentId: req.AgentID, ToolProfile: req.ToolProfile, ToolLoop: &corepb.AgentToolLoopOptions{ ToolsEnabled: toolsEnabled, MaxToolIterations: maxIters, MaxTotalToolCalls: maxCalls, TotalTimeoutSeconds: req.TimeoutSeconds, }, ResponseContract: &corepb.AgentResponseContract{ Format: format, JsonSchema: req.JSONSchema, RepairInvalidOutput: req.Repair, }, RequestId: req.RequestID, IdempotencyKey: req.IdempotencyKey, SupportsDeferred: false, Metadata: req.Metadata, }) if err != nil { return RunResult{}, err } if resp.Error != "" { return RunResult{}, fmt.Errorf("core RunAgent response error: %s", resp.Error) } if resp.Status == corepb.AnswerStatus_ANSWER_STATUS_PENDING { return RunResult{}, fmt.Errorf("unexpected deferred RunAgent response") } out := RunResult{Content: resp.Content, DurationMS: resp.DurationMs} if resp.Usage != nil { out.ToolsUsed = resp.Usage.ToolsUsed } if !toolsEnabled && len(out.ToolsUsed) != 0 { return RunResult{}, fmt.Errorf("tools used despite tools-disabled request") } return out, nil }