Refine execution target mapping helpers and coverage across usecase, HTTP, and LLM
This commit is contained in:
@@ -63,18 +63,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var model *domain.ExecutionTarget
|
||||
if req.Model != nil {
|
||||
model = &domain.ExecutionTarget{
|
||||
Endpoint: req.Model.Endpoint,
|
||||
Model: req.Model.Model,
|
||||
Temperature: req.Model.Temperature,
|
||||
MaxTokens: req.Model.MaxTokens,
|
||||
TopP: req.Model.TopP,
|
||||
TimeoutSeconds: req.Model.TimeoutSeconds,
|
||||
ServiceTier: req.Model.ServiceTier,
|
||||
ReasoningEffort: req.Model.ReasoningEffort,
|
||||
APIKeyEnv: req.Model.APIKeyEnv,
|
||||
ExtraParams: req.Model.ExtraParams,
|
||||
}
|
||||
model = executionTargetFromModelOverrideDTO(req.Model)
|
||||
}
|
||||
|
||||
res, err := h.runner.Run(r.Context(), domain.RunRequest{
|
||||
@@ -110,19 +99,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
SelectedProfileID: res.SelectedProfileID,
|
||||
ModelName: res.ModelName,
|
||||
Endpoint: res.Endpoint,
|
||||
ModelParams: modelParamsDTO{
|
||||
Endpoint: res.EffectiveModelParams.Endpoint,
|
||||
Model: res.EffectiveModelParams.Model,
|
||||
Temperature: res.EffectiveModelParams.Temperature,
|
||||
MaxTokens: res.EffectiveModelParams.MaxTokens,
|
||||
TopP: res.EffectiveModelParams.TopP,
|
||||
TimeoutSeconds: res.EffectiveModelParams.TimeoutSeconds,
|
||||
ServiceTier: res.EffectiveModelParams.ServiceTier,
|
||||
ReasoningEffort: res.EffectiveModelParams.ReasoningEffort,
|
||||
APIKeyEnv: res.EffectiveModelParams.APIKeyEnv,
|
||||
ExtraParams: res.EffectiveModelParams.ExtraParams,
|
||||
},
|
||||
InputHashes: res.InputHashes,
|
||||
ModelParams: modelParamsDTOFromExecutionTarget(res.EffectiveModelParams),
|
||||
InputHashes: res.InputHashes,
|
||||
Usage: tokenUsageDTO{
|
||||
PromptTokens: res.Usage.PromptTokens,
|
||||
CompletionTokens: res.Usage.CompletionTokens,
|
||||
@@ -143,6 +121,39 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func executionTargetFromModelOverrideDTO(dto *modelOverrideRequestDTO) *domain.ExecutionTarget {
|
||||
if dto == nil {
|
||||
return nil
|
||||
}
|
||||
return &domain.ExecutionTarget{
|
||||
Endpoint: dto.Endpoint,
|
||||
Model: dto.Model,
|
||||
Temperature: dto.Temperature,
|
||||
MaxTokens: dto.MaxTokens,
|
||||
TopP: dto.TopP,
|
||||
TimeoutSeconds: dto.TimeoutSeconds,
|
||||
ServiceTier: dto.ServiceTier,
|
||||
ReasoningEffort: dto.ReasoningEffort,
|
||||
APIKeyEnv: dto.APIKeyEnv,
|
||||
ExtraParams: dto.ExtraParams,
|
||||
}
|
||||
}
|
||||
|
||||
func modelParamsDTOFromExecutionTarget(target domain.ExecutionTarget) modelParamsDTO {
|
||||
return modelParamsDTO{
|
||||
Endpoint: target.Endpoint,
|
||||
Model: target.Model,
|
||||
Temperature: target.Temperature,
|
||||
MaxTokens: target.MaxTokens,
|
||||
TopP: target.TopP,
|
||||
TimeoutSeconds: target.TimeoutSeconds,
|
||||
ServiceTier: target.ServiceTier,
|
||||
ReasoningEffort: target.ReasoningEffort,
|
||||
APIKeyEnv: target.APIKeyEnv,
|
||||
ExtraParams: target.ExtraParams,
|
||||
}
|
||||
}
|
||||
|
||||
func mapValidation(v domain.ValidationResult) validationDTO {
|
||||
return validationDTO{
|
||||
Status: string(v.Status),
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -173,6 +174,136 @@ func TestHandlerPostRunsSuccessUsingPromptDefaultProfile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlerModelOverrideMapsAllSupportedExecutionFields(t *testing.T) {
|
||||
r := &fakeRunner{result: &domain.RunResult{
|
||||
Artifact: domain.Artifact{Body: []byte("ok")},
|
||||
Validation: domain.ValidationResult{Status: domain.ValidationPassed, Mode: domain.ValidationBasic, IsValid: true},
|
||||
EffectiveModelParams: domain.ExecutionTarget{Endpoint: "http://llm/v1", Model: "m1"},
|
||||
}}
|
||||
h := NewHandler(r)
|
||||
|
||||
reqBody := `{
|
||||
"prompt_id": "prompt-1",
|
||||
"inputs": {"transcript": {"type": "file", "uri": "./t.md"}},
|
||||
"model": {
|
||||
"endpoint": "http://override/v1",
|
||||
"model": "override-model",
|
||||
"temperature": 0.6,
|
||||
"max_tokens": 250,
|
||||
"top_p": 0.85,
|
||||
"timeout_seconds": 33,
|
||||
"service_tier": "flex",
|
||||
"reasoning_effort": "medium",
|
||||
"api_key_env": "SCRIPTORIUM_API_KEY",
|
||||
"extra_params": {"provider_option":"on"}
|
||||
}
|
||||
}`
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/runs", bytes.NewBufferString(reqBody))
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d body=%s", w.Code, w.Body.String())
|
||||
}
|
||||
if r.last.Execution == nil {
|
||||
t.Fatalf("expected execution override in run request")
|
||||
}
|
||||
got := r.last.Execution
|
||||
if got.Endpoint != "http://override/v1" ||
|
||||
got.Model != "override-model" ||
|
||||
got.Temperature != 0.6 ||
|
||||
got.MaxTokens != 250 ||
|
||||
got.TopP != 0.85 ||
|
||||
got.TimeoutSeconds != 33 ||
|
||||
got.ServiceTier != "flex" ||
|
||||
got.ReasoningEffort != "medium" ||
|
||||
got.APIKeyEnv != "SCRIPTORIUM_API_KEY" {
|
||||
t.Fatalf("unexpected mapped execution target: %+v", got)
|
||||
}
|
||||
if !reflect.DeepEqual(got.ExtraParams, map[string]string{"provider_option": "on"}) {
|
||||
t.Fatalf("unexpected mapped extra_params: %#v", got.ExtraParams)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlerResponseMetadataModelParamsIncludesAllSupportedFields(t *testing.T) {
|
||||
r := &fakeRunner{result: &domain.RunResult{
|
||||
Artifact: domain.Artifact{
|
||||
Name: "output",
|
||||
ContentType: "text/plain",
|
||||
Body: []byte("ok"),
|
||||
Size: 2,
|
||||
Hash: "abc",
|
||||
},
|
||||
Validation: domain.ValidationResult{Status: domain.ValidationPassed, Mode: domain.ValidationBasic, IsValid: true},
|
||||
EffectiveModelParams: domain.ExecutionTarget{
|
||||
Endpoint: "http://llm/v1",
|
||||
Model: "gpt-test",
|
||||
Temperature: 0.4,
|
||||
MaxTokens: 321,
|
||||
TopP: 0.7,
|
||||
TimeoutSeconds: 45,
|
||||
ServiceTier: "priority",
|
||||
ReasoningEffort: "high",
|
||||
APIKeyEnv: "SCRIPTORIUM_API_KEY",
|
||||
ExtraParams: map[string]string{
|
||||
"provider_option": "on",
|
||||
},
|
||||
},
|
||||
}}
|
||||
h := NewHandler(r)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/runs", bytes.NewBufferString(`{"prompt_id":"p","inputs":{"x":{"type":"file","uri":"a"}}}`))
|
||||
w := httptest.NewRecorder()
|
||||
h.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d body=%s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
var resp map[string]any
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("invalid JSON response: %v", err)
|
||||
}
|
||||
metadata := resp["metadata"].(map[string]any)
|
||||
params := metadata["model_params"].(map[string]any)
|
||||
|
||||
if params["endpoint"] != "http://llm/v1" {
|
||||
t.Fatalf("unexpected endpoint: %#v", params["endpoint"])
|
||||
}
|
||||
if params["model"] != "gpt-test" {
|
||||
t.Fatalf("unexpected model: %#v", params["model"])
|
||||
}
|
||||
if params["temperature"] != 0.4 {
|
||||
t.Fatalf("unexpected temperature: %#v", params["temperature"])
|
||||
}
|
||||
if params["max_tokens"] != float64(321) {
|
||||
t.Fatalf("unexpected max_tokens: %#v", params["max_tokens"])
|
||||
}
|
||||
if params["top_p"] != 0.7 {
|
||||
t.Fatalf("unexpected top_p: %#v", params["top_p"])
|
||||
}
|
||||
if params["timeout_seconds"] != float64(45) {
|
||||
t.Fatalf("unexpected timeout_seconds: %#v", params["timeout_seconds"])
|
||||
}
|
||||
if params["service_tier"] != "priority" {
|
||||
t.Fatalf("unexpected service_tier: %#v", params["service_tier"])
|
||||
}
|
||||
if params["reasoning_effort"] != "high" {
|
||||
t.Fatalf("unexpected reasoning_effort: %#v", params["reasoning_effort"])
|
||||
}
|
||||
if params["api_key_env"] != "SCRIPTORIUM_API_KEY" {
|
||||
t.Fatalf("unexpected api_key_env: %#v", params["api_key_env"])
|
||||
}
|
||||
extraParams, ok := params["extra_params"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("expected extra_params object, got %#v", params["extra_params"])
|
||||
}
|
||||
if extraParams["provider_option"] != "on" {
|
||||
t.Fatalf("unexpected extra_params.provider_option: %#v", extraParams["provider_option"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlerInvalidJSON(t *testing.T) {
|
||||
h := NewHandler(&fakeRunner{})
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/runs", bytes.NewBufferString("{"))
|
||||
|
||||
Reference in New Issue
Block a user