Add structured output repair support
This commit is contained in:
@@ -344,7 +344,7 @@ git diff --check
|
|||||||
- Capacity and context classifications remain unchanged and more specific.
|
- Capacity and context classifications remain unchanged and more specific.
|
||||||
- Security tests prove selected credentials and bearer tokens are not leaked.
|
- Security tests prove selected credentials and bearer tokens are not leaked.
|
||||||
|
|
||||||
## Stage 5: Add Adapter-Level Structured Repair Support
|
## Stage 5: Add Adapter-Level Structured Repair Support ✅
|
||||||
|
|
||||||
### Goal
|
### Goal
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type StructuredCompletionRequest struct {
|
type StructuredCompletionRequest struct {
|
||||||
StageName string `json:"stage_name"`
|
StageName string `json:"stage_name"`
|
||||||
PromptID string `json:"prompt_id,omitempty"`
|
PromptID string `json:"prompt_id,omitempty"`
|
||||||
PromptVersion string `json:"prompt_version,omitempty"`
|
PromptVersion string `json:"prompt_version,omitempty"`
|
||||||
ProfileID string `json:"profile_id,omitempty"`
|
ProfileID string `json:"profile_id,omitempty"`
|
||||||
SessionID string `json:"session_id,omitempty"`
|
SessionID string `json:"session_id,omitempty"`
|
||||||
Inputs LLMInputSet `json:"inputs,omitempty"`
|
Inputs LLMInputSet `json:"inputs,omitempty"`
|
||||||
Vars map[string]any `json:"vars,omitempty"`
|
Vars map[string]any `json:"vars,omitempty"`
|
||||||
|
StructuredOutputRepairAttempts *int `json:"structured_output_repair_attempts,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type StructuredCompletionResponse struct {
|
type StructuredCompletionResponse struct {
|
||||||
@@ -26,6 +27,7 @@ type StructuredCompletionResponse struct {
|
|||||||
PromptTokens int `json:"prompt_tokens,omitempty"`
|
PromptTokens int `json:"prompt_tokens,omitempty"`
|
||||||
CompletionTokens int `json:"completion_tokens,omitempty"`
|
CompletionTokens int `json:"completion_tokens,omitempty"`
|
||||||
TotalTokens int `json:"total_tokens,omitempty"`
|
TotalTokens int `json:"total_tokens,omitempty"`
|
||||||
|
RepairAttempts int `json:"repair_attempts,omitempty"`
|
||||||
Debug *LLMDebugMaterial `json:"debug,omitempty"`
|
Debug *LLMDebugMaterial `json:"debug,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -116,6 +116,9 @@ func (c *PromptKitClient) CompleteStructured(ctx context.Context, req contracts.
|
|||||||
if err := validateOutputTarget(out); err != nil {
|
if err := validateOutputTarget(out); err != nil {
|
||||||
return contracts.StructuredCompletionResponse{}, err
|
return contracts.StructuredCompletionResponse{}, err
|
||||||
}
|
}
|
||||||
|
if req.StructuredOutputRepairAttempts != nil && (*req.StructuredOutputRepairAttempts < 0 || *req.StructuredOutputRepairAttempts > 3) {
|
||||||
|
return contracts.StructuredCompletionResponse{}, fmt.Errorf("structured output repair attempts must be between zero and three")
|
||||||
|
}
|
||||||
promptID := strings.TrimSpace(req.PromptID)
|
promptID := strings.TrimSpace(req.PromptID)
|
||||||
if promptID == "" {
|
if promptID == "" {
|
||||||
return contracts.StructuredCompletionResponse{}, fmt.Errorf("structured completion prompt_id must not be empty")
|
return contracts.StructuredCompletionResponse{}, fmt.Errorf("structured completion prompt_id must not be empty")
|
||||||
@@ -138,6 +141,15 @@ func (c *PromptKitClient) CompleteStructured(ctx context.Context, req contracts.
|
|||||||
Vars: promptKitVars(req, sessionID),
|
Vars: promptKitVars(req, sessionID),
|
||||||
Execution: execution,
|
Execution: execution,
|
||||||
}
|
}
|
||||||
|
if req.StructuredOutputRepairAttempts != nil {
|
||||||
|
inspection, err := c.engine.InspectPrompt(ctx, promptID, strings.TrimSpace(req.PromptVersion))
|
||||||
|
if err != nil {
|
||||||
|
return contracts.StructuredCompletionResponse{}, fmt.Errorf("inspect PromptKit prompt %q: %v", promptID, redactPromptKitError(err))
|
||||||
|
}
|
||||||
|
contract := inspection.OutputContract
|
||||||
|
contract.RepairAttempts = *req.StructuredOutputRepairAttempts
|
||||||
|
runReq.Validation = &contract
|
||||||
|
}
|
||||||
prepared, err := c.engine.PrepareExecution(ctx, runReq)
|
prepared, err := c.engine.PrepareExecution(ctx, runReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ctxErr := ctx.Err(); ctxErr != nil {
|
if ctxErr := ctx.Err(); ctxErr != nil {
|
||||||
@@ -242,6 +254,7 @@ func (c *PromptKitClient) responseFromResult(result *promptkit.RunResult, prepar
|
|||||||
PromptTokens: result.Usage.PromptTokens,
|
PromptTokens: result.Usage.PromptTokens,
|
||||||
CompletionTokens: result.Usage.CompletionTokens,
|
CompletionTokens: result.Usage.CompletionTokens,
|
||||||
TotalTokens: result.Usage.TotalTokens,
|
TotalTokens: result.Usage.TotalTokens,
|
||||||
|
RepairAttempts: result.Validation.RepairAttempts,
|
||||||
Debug: promptKitDebugMaterial(prepared, result),
|
Debug: promptKitDebugMaterial(prepared, result),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -667,6 +667,20 @@ func TestPromptKitClientMapsHTTPGenerationErrorToApplicationBoundary(t *testing.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPromptKitClientRejectsInvalidRepairAttemptOverride(t *testing.T) {
|
||||||
|
for _, attempts := range []int{-1, 4} {
|
||||||
|
t.Run("invalid", func(t *testing.T) {
|
||||||
|
fake := &fakePromptKitLLM{content: `{"ok":true}`}
|
||||||
|
client := newTestPromptKitClient(t, fake)
|
||||||
|
var out map[string]any
|
||||||
|
_, err := client.CompleteStructured(context.Background(), contracts.StructuredCompletionRequest{PromptID: "adapter.test", StructuredOutputRepairAttempts: &attempts}, &out)
|
||||||
|
if err == nil || fake.calls != 0 {
|
||||||
|
t.Fatalf("error=%v calls=%d", err, fake.calls)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPromptKitClientCheckpointFingerprintTracksFallbackProfileAssets(t *testing.T) {
|
func TestPromptKitClientCheckpointFingerprintTracksFallbackProfileAssets(t *testing.T) {
|
||||||
fingerprintFor := func(content string) CheckpointFingerprint {
|
fingerprintFor := func(content string) CheckpointFingerprint {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|||||||
Reference in New Issue
Block a user