mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-10-09 17:34:35 +02:00
feat: add TypeSafe Jev as HITL audit backend
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>
This commit is contained in:
1 parent
3aa9274675
commit
38b96ec67a
30 files changed
+1372
-21
No files matched your search
@@ -0,0 +1,196 @@
|
||||
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
|
||||
}
|
||||
Reference in new issue
Block a user