mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-09-30 05:02:06 +02:00
Let audit_agent approve or reject with one System One call instead of chat JSON, while keeping the OpenAI-compatible backend as an option. Co-authored-by: Cursor <cursoragent@cursor.com>
197 lines
4.6 KiB
Go
197 lines
4.6 KiB
Go
package typesafe
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
DefaultBaseURL = "https://api.typesafe.ai"
|
|
DefaultModel = "jev-latest"
|
|
)
|
|
|
|
// Client calls TypeSafe System One (Jev).
|
|
type Client struct {
|
|
httpClient *http.Client
|
|
baseURL string
|
|
apiKey string
|
|
model string
|
|
}
|
|
|
|
// APIError is a non-2xx TypeSafe HTTP response.
|
|
type APIError struct {
|
|
StatusCode int
|
|
Body string
|
|
}
|
|
|
|
func (e *APIError) Error() string {
|
|
return fmt.Sprintf("typesafe api error: status=%d body=%s", e.StatusCode, e.Body)
|
|
}
|
|
|
|
// NewClient builds a System One client. Empty baseURL/model use TypeSafe defaults.
|
|
func NewClient(baseURL, apiKey, model string, httpClient *http.Client) *Client {
|
|
if httpClient == nil {
|
|
httpClient = &http.Client{Timeout: 90 * time.Second}
|
|
}
|
|
baseURL = strings.TrimSuffix(strings.TrimSpace(baseURL), "/")
|
|
if baseURL == "" {
|
|
baseURL = DefaultBaseURL
|
|
}
|
|
model = strings.TrimSpace(model)
|
|
if model == "" {
|
|
model = DefaultModel
|
|
}
|
|
return &Client{
|
|
httpClient: httpClient,
|
|
baseURL: baseURL,
|
|
apiKey: strings.TrimSpace(apiKey),
|
|
model: model,
|
|
}
|
|
}
|
|
|
|
// Question is a typed System One question (noul / choice / score).
|
|
type Question map[string]any
|
|
|
|
// Noul builds a yes/no question.
|
|
func Noul(instructions string, trueMean, falseMean string) Question {
|
|
q := Question{
|
|
"type": "noul",
|
|
"instructions": instructions,
|
|
}
|
|
if strings.TrimSpace(trueMean) != "" || strings.TrimSpace(falseMean) != "" {
|
|
q["criteria"] = map[string]string{
|
|
"true": trueMean,
|
|
"false": falseMean,
|
|
}
|
|
}
|
|
return q
|
|
}
|
|
|
|
// Choice builds a closed-set question.
|
|
func Choice(instructions string, criteria map[string]string) Question {
|
|
return Question{
|
|
"type": "choice",
|
|
"instructions": instructions,
|
|
"criteria": criteria,
|
|
}
|
|
}
|
|
|
|
// Result is a System One evaluation response.
|
|
type Result struct {
|
|
Model string `json:"model"`
|
|
Answers map[string]map[string]any `json:"answers"`
|
|
Usage Usage `json:"usage"`
|
|
}
|
|
|
|
// Usage reports token counts.
|
|
type Usage struct {
|
|
InputTokens int `json:"input_tokens"`
|
|
OutputTokens int `json:"output_tokens"`
|
|
}
|
|
|
|
// Noul returns the probability that question id is yes.
|
|
func (r *Result) Noul(id string) float64 {
|
|
if r == nil {
|
|
return 0
|
|
}
|
|
ans, ok := r.Answers[id]
|
|
if !ok || ans == nil {
|
|
return 0
|
|
}
|
|
switch v := ans["noul"].(type) {
|
|
case float64:
|
|
return v
|
|
case json.Number:
|
|
f, _ := v.Float64()
|
|
return f
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// Choice returns the selected option and confidence.
|
|
func (r *Result) Choice(id string) (choice string, confidence float64) {
|
|
if r == nil {
|
|
return "", 0
|
|
}
|
|
ans, ok := r.Answers[id]
|
|
if !ok || ans == nil {
|
|
return "", 0
|
|
}
|
|
choice, _ = ans["choice"].(string)
|
|
switch v := ans["confidence"].(type) {
|
|
case float64:
|
|
confidence = v
|
|
case json.Number:
|
|
confidence, _ = v.Float64()
|
|
}
|
|
return strings.TrimSpace(choice), confidence
|
|
}
|
|
|
|
type systemOneRequest struct {
|
|
State any `json:"state"`
|
|
Model string `json:"model"`
|
|
Questions map[string]Question `json:"questions"`
|
|
}
|
|
|
|
// SystemOne evaluates state against questions.
|
|
func (c *Client) SystemOne(ctx context.Context, state any, questions map[string]Question) (*Result, error) {
|
|
if c == nil {
|
|
return nil, fmt.Errorf("typesafe client is not initialized")
|
|
}
|
|
if strings.TrimSpace(c.apiKey) == "" {
|
|
return nil, fmt.Errorf("typesafe api key is empty")
|
|
}
|
|
if len(questions) == 0 {
|
|
return nil, fmt.Errorf("typesafe questions are empty")
|
|
}
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
|
|
body, err := json.Marshal(systemOneRequest{
|
|
State: state,
|
|
Model: c.model,
|
|
Questions: questions,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal typesafe payload: %w", err)
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+"/v1/systemone", bytes.NewReader(body))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("build typesafe request: %w", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Authorization", "Bearer "+c.apiKey)
|
|
|
|
resp, err := c.httpClient.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("call typesafe api: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read typesafe response: %w", err)
|
|
}
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, &APIError{StatusCode: resp.StatusCode, Body: string(respBody)}
|
|
}
|
|
|
|
var out Result
|
|
if err := json.Unmarshal(respBody, &out); err != nil {
|
|
return nil, fmt.Errorf("decode typesafe response: %w", err)
|
|
}
|
|
if out.Answers == nil {
|
|
out.Answers = map[string]map[string]any{}
|
|
}
|
|
return &out, nil
|
|
}
|