l-ai-agent-kaya/internal/inference/core.go
alex adb6c5ab8a
All checks were successful
ci/woodpecker/manual/woodpecker Pipeline was successful
Initial Kaya board-mention agent daemon.
Thin WS client to l-ai-tasks that drafts short core RunAgent replies on @kaya mentions.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-21 23:07:24 +03:00

100 lines
2.6 KiB
Go

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})
}
toolsEnabled := req.ToolsEnabled
maxIters := uint32(1)
maxCalls := uint32(0)
if toolsEnabled {
maxIters = 12
maxCalls = 20
}
resp, err := r.api.RunAgent(r.auth(ctx), &corepb.AgentRequest{
Messages: msgs,
ModelName: req.Model,
AgentId: req.AgentID,
ToolLoop: &corepb.AgentToolLoopOptions{
ToolsEnabled: toolsEnabled,
MaxToolIterations: maxIters,
MaxTotalToolCalls: maxCalls,
TotalTimeoutSeconds: req.TimeoutSeconds,
},
ResponseContract: &corepb.AgentResponseContract{
Format: corepb.AgentOutputFormat_AGENT_OUTPUT_FORMAT_TEXT,
},
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
}