- Add RoutingWorkflow: generic state machine executor for WorkflowSpec - Add LLM Router: natural language → WorkflowSpec generation - Add RetrieveMemoryActivity: query poimen-memory for context - Add activities: AnalyzeCode, SecurityScan, GenerateReport, Notify, etc. - Add agent-prompts/router: LLM prompt documentation - Extend starter with --route flag for routing workflows - Remove orchestrator job (trigger via API/message instead) - Clean up: move docs to Desktop, add .gitignore for *.md
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
package routing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
// llmBaseURL is the base URL for the LLM API
|
||||
llmBaseURL string
|
||||
)
|
||||
|
||||
func init() {
|
||||
llmBaseURL = os.Getenv("LOCAL_LLM_BASE_URL")
|
||||
if llmBaseURL == "" {
|
||||
llmBaseURL = "https://api.riotpiao.com"
|
||||
}
|
||||
}
|
||||
|
||||
// LLMClient is a simple LLM client for routing
|
||||
type LLMClient struct {
|
||||
baseURL string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// NewLLMClient creates a new LLM client
|
||||
func NewLLMClient() *LLMClient {
|
||||
return &LLMClient{
|
||||
baseURL: llmBaseURL,
|
||||
httpClient: &http.Client{},
|
||||
}
|
||||
}
|
||||
|
||||
// llmRequest is the request body for the OpenAI-compatible API
|
||||
type llmRequest struct {
|
||||
Model string `json:"model"`
|
||||
Messages []llmMessage `json:"messages"`
|
||||
Stream bool `json:"stream"`
|
||||
}
|
||||
|
||||
type llmMessage struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// llmResponse is the response from the OpenAI-compatible API
|
||||
type llmResponse struct {
|
||||
Choices []struct {
|
||||
Message struct {
|
||||
Content string `json:"content"`
|
||||
} `json:"message"`
|
||||
} `json:"choices"`
|
||||
}
|
||||
|
||||
// Chat sends a chat completion request
|
||||
func (c *LLMClient) Chat(ctx context.Context, systemPrompt, userMessage string) (string, error) {
|
||||
req := llmRequest{
|
||||
Model: "reasoning",
|
||||
Messages: []llmMessage{
|
||||
{Role: "system", Content: systemPrompt},
|
||||
{Role: "user", Content: userMessage},
|
||||
},
|
||||
Stream: false,
|
||||
}
|
||||
|
||||
reqBody, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal request: %w", err)
|
||||
}
|
||||
|
||||
httpReq, err := http.NewRequestWithContext(ctx, "POST",
|
||||
fmt.Sprintf("%s/v1/chat/completions", c.baseURL),
|
||||
bytes.NewReader(reqBody))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create HTTP request: %w", err)
|
||||
}
|
||||
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.httpClient.Do(httpReq)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to connect to LLM API at %s: %w", c.baseURL, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read response body: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("LLM API returned status %d: %s", resp.StatusCode, string(respBody))
|
||||
}
|
||||
|
||||
var respObj llmResponse
|
||||
if err := json.Unmarshal(respBody, &respObj); err != nil {
|
||||
return "", fmt.Errorf("failed to unmarshal response: %w", err)
|
||||
}
|
||||
|
||||
if len(respObj.Choices) == 0 {
|
||||
return "", fmt.Errorf("no choices in response from LLM API")
|
||||
}
|
||||
|
||||
return respObj.Choices[0].Message.Content, nil
|
||||
}
|
||||
Reference in New Issue
Block a user