feat: implement AI gateway phase one runtime
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
package clients
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SimulationClient struct{}
|
||||
|
||||
func (c SimulationClient) Run(ctx context.Context, request Request) (Response, error) {
|
||||
_ = ctx
|
||||
profile := simulationProfile(request)
|
||||
if profile == "retryable_failure" {
|
||||
return Response{}, &ClientError{Code: "server_error", Message: "simulated retryable failure", Retryable: true}
|
||||
}
|
||||
if profile == "fatal_failure" || profile == "non_retryable_failure" {
|
||||
return Response{}, &ClientError{Code: "bad_request", Message: "simulated non-retryable failure", Retryable: false}
|
||||
}
|
||||
return Response{
|
||||
Result: simulatedResult(request),
|
||||
Usage: simulatedUsage(request),
|
||||
Progress: simulatedProgress(request),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func simulationProfile(request Request) string {
|
||||
if value := stringValue(request.Candidate.Credentials, "simulationFailure"); value != "" {
|
||||
return value
|
||||
}
|
||||
if value := stringValue(request.Candidate.PlatformConfig, "simulationFailure"); value != "" {
|
||||
return value
|
||||
}
|
||||
if value := stringValue(request.Body, "simulationProfile"); value != "" {
|
||||
return value
|
||||
}
|
||||
if value := stringValue(request.Body, "testProfile"); value != "" {
|
||||
return value
|
||||
}
|
||||
return "success"
|
||||
}
|
||||
|
||||
func simulatedResult(request Request) map[string]any {
|
||||
switch request.Kind {
|
||||
case "chat.completions":
|
||||
return map[string]any{
|
||||
"id": "chatcmpl-simulated",
|
||||
"object": "chat.completion",
|
||||
"created": nowUnix(),
|
||||
"model": request.Model,
|
||||
"choices": []any{map[string]any{
|
||||
"index": 0,
|
||||
"finish_reason": "stop",
|
||||
"message": map[string]any{
|
||||
"role": "assistant",
|
||||
"content": fmt.Sprintf("simulation response from %s", request.Candidate.Provider),
|
||||
},
|
||||
}},
|
||||
"usage": map[string]any{"prompt_tokens": 12, "completion_tokens": 8, "total_tokens": 20},
|
||||
}
|
||||
case "responses":
|
||||
return map[string]any{
|
||||
"id": "resp-simulated",
|
||||
"object": "response",
|
||||
"created_at": nowUnix(),
|
||||
"model": request.Model,
|
||||
"output_text": fmt.Sprintf("simulation response from %s", request.Candidate.Provider),
|
||||
"usage": map[string]any{"input_tokens": 12, "output_tokens": 8, "total_tokens": 20},
|
||||
}
|
||||
case "images.edits":
|
||||
return map[string]any{
|
||||
"id": "img-edit-simulated",
|
||||
"created": nowUnix(),
|
||||
"model": request.Model,
|
||||
"data": []any{map[string]any{
|
||||
"url": "/static/simulation/image-edit.png",
|
||||
"revised_prompt": firstNonEmptyPrompt(request.Body, "simulation image edit"),
|
||||
}},
|
||||
}
|
||||
default:
|
||||
return map[string]any{
|
||||
"id": "img-simulated",
|
||||
"created": nowUnix(),
|
||||
"model": request.Model,
|
||||
"data": []any{map[string]any{
|
||||
"url": "/static/simulation/image.png",
|
||||
"revised_prompt": firstNonEmptyPrompt(request.Body, "simulation image"),
|
||||
}},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func simulatedUsage(request Request) Usage {
|
||||
if request.ModelType == "chat" || request.Kind == "responses" {
|
||||
return Usage{InputTokens: 12, OutputTokens: 8, TotalTokens: 20}
|
||||
}
|
||||
return Usage{}
|
||||
}
|
||||
|
||||
func simulatedProgress(request Request) []Progress {
|
||||
provider := request.Candidate.Provider
|
||||
if provider == "" {
|
||||
provider = "simulation"
|
||||
}
|
||||
return []Progress{
|
||||
{Phase: "normalizing", Progress: 0.2, Message: "request normalized", Payload: map[string]any{"provider": provider}},
|
||||
{Phase: "submitting", Progress: 0.55, Message: "simulation client submitted", Payload: map[string]any{"clientId": request.Candidate.ClientID}},
|
||||
{Phase: "fetching_result", Progress: 0.85, Message: "simulation result ready", Payload: map[string]any{"kind": request.Kind}},
|
||||
}
|
||||
}
|
||||
|
||||
func firstNonEmptyPrompt(body map[string]any, fallback string) string {
|
||||
for _, key := range []string{"prompt", "input"} {
|
||||
if value := strings.TrimSpace(stringValue(body, key)); value != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
Reference in New Issue
Block a user