fix: ResetData provider - reasoning chains, URL paths, error parsing, model list

- Add Reasoning/ReasoningContent fields to ResetDataMessage struct
- Wire reasoning extraction to TaskResponse.Reasoning in ExecuteTask
- Fix double /v1 in makeRequest and testConnection URL construction
- Handle both ResetData error formats (flat string and nested object)
- Update supported models to actual ResetData beta inventory

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
anthonyrawlins
2026-02-26 19:31:36 +11:00
parent 17673c38a6
commit 8fa636acbb

View File

@@ -32,6 +32,8 @@ type ResetDataRequest struct {
type ResetDataMessage struct {
Role string `json:"role"` // system, user, assistant
Content string `json:"content"`
Reasoning string `json:"reasoning,omitempty"` // reasoning chain (GLM-4.7, GPT-OSS, Nemotron 3 Nano)
ReasoningContent string `json:"reasoning_content,omitempty"` // alternate reasoning field (GPT-OSS)
}
// ResetDataResponse represents a response from ResetData LaaS API
@@ -107,7 +109,7 @@ func (p *ResetDataProvider) ExecuteTask(ctx context.Context, request *TaskReques
}
// Execute the request
response, err := p.makeRequest(ctx, "/v1/chat/completions", resetDataReq)
response, err := p.makeRequest(ctx, "/chat/completions", resetDataReq)
if err != nil {
return nil, err
}
@@ -122,6 +124,12 @@ func (p *ResetDataProvider) ExecuteTask(ctx context.Context, request *TaskReques
choice := response.Choices[0]
responseText := choice.Message.Content
// Extract reasoning chain - prefer Reasoning field, fall back to ReasoningContent
reasoning := choice.Message.Reasoning
if reasoning == "" {
reasoning = choice.Message.ReasoningContent
}
// Parse response for actions and artifacts
actions, artifacts := p.parseResponseForActions(responseText, request)
@@ -132,6 +140,7 @@ func (p *ResetDataProvider) ExecuteTask(ctx context.Context, request *TaskReques
ModelUsed: response.Model,
Provider: "resetdata",
Response: responseText,
Reasoning: reasoning,
Actions: actions,
Artifacts: artifacts,
StartTime: startTime,
@@ -405,7 +414,7 @@ func (p *ResetDataProvider) makeRequest(ctx context.Context, endpoint string, re
// testConnection tests the connection to ResetData API
func (p *ResetDataProvider) testConnection(ctx context.Context) error {
url := strings.TrimSuffix(p.config.Endpoint, "/") + "/v1/models"
url := strings.TrimSuffix(p.config.Endpoint, "/") + "/models"
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return err
@@ -429,52 +438,92 @@ func (p *ResetDataProvider) testConnection(ctx context.Context) error {
// getSupportedModels returns a list of supported ResetData models
func (p *ResetDataProvider) getSupportedModels() []string {
// Common models available through ResetData LaaS
// Models available through ResetData beta (as of 2026-02)
return []string{
"llama3.1:8b", "llama3.1:70b",
"mistral:7b", "mixtral:8x7b",
"qwen2:7b", "qwen2:72b",
"gemma:7b", "gemma2:9b",
"codellama:7b", "codellama:13b",
"zai-org/glm-4.7-fp8",
"openai/gpt-oss-120b",
"google/gemma-3-27b-it",
"meta/llama-3.1-8b-instruct",
"nvidia/nemotron-3-nano-30b-a3b",
"nvidia/cosmos-reason2-8b",
"nvidia/nemotron-nano-2-vl",
}
}
// handleHTTPError converts HTTP errors to provider errors
func (p *ResetDataProvider) handleHTTPError(statusCode int, body []byte) *ProviderError {
bodyStr := string(body)
// Extract a human-readable error message from the response body.
// ResetData returns two formats:
// Format 1 (auth): {"success":false,"error":"Invalid or expired token"}
// Format 2 (model/validation): {"error":{"message":"...","type":"...","code":"..."}}
errMsg := p.extractErrorMessage(body)
switch statusCode {
case http.StatusUnauthorized:
return &ProviderError{
Code: "UNAUTHORIZED",
Message: "Invalid ResetData API key",
Details: bodyStr,
Message: fmt.Sprintf("ResetData auth failed: %s", errMsg),
Details: string(body),
Retryable: false,
}
case http.StatusTooManyRequests:
return &ProviderError{
Code: "RATE_LIMIT_EXCEEDED",
Message: "ResetData API rate limit exceeded",
Details: bodyStr,
Message: fmt.Sprintf("ResetData rate limit: %s", errMsg),
Details: string(body),
Retryable: true,
}
case http.StatusInternalServerError, http.StatusBadGateway, http.StatusServiceUnavailable:
return &ProviderError{
Code: "SERVICE_UNAVAILABLE",
Message: "ResetData API service unavailable",
Details: bodyStr,
Message: fmt.Sprintf("ResetData unavailable: %s", errMsg),
Details: string(body),
Retryable: true,
}
default:
return &ProviderError{
Code: "API_ERROR",
Message: fmt.Sprintf("ResetData API error (status %d)", statusCode),
Details: bodyStr,
Message: fmt.Sprintf("ResetData error (status %d): %s", statusCode, errMsg),
Details: string(body),
Retryable: true,
}
}
}
// extractErrorMessage parses error details from ResetData API response bodies.
func (p *ResetDataProvider) extractErrorMessage(body []byte) string {
// Try Format 2: {"error":{"message":"...","type":"...","code":"..."}}
var nestedErr struct {
Error struct {
Message string `json:"message"`
Type string `json:"type"`
Code string `json:"code"`
} `json:"error"`
}
if err := json.Unmarshal(body, &nestedErr); err == nil && nestedErr.Error.Message != "" {
if nestedErr.Error.Type != "" {
return fmt.Sprintf("%s (%s)", nestedErr.Error.Message, nestedErr.Error.Type)
}
return nestedErr.Error.Message
}
// Try Format 1: {"success":false,"error":"string message"}
var flatErr struct {
Success bool `json:"success"`
Error string `json:"error"`
}
if err := json.Unmarshal(body, &flatErr); err == nil && flatErr.Error != "" {
return flatErr.Error
}
// Fallback: return raw body truncated
s := string(body)
if len(s) > 200 {
s = s[:200] + "..."
}
return s
}
// parseResponseForActions extracts actions from the response text
func (p *ResetDataProvider) parseResponseForActions(response string, request *TaskRequest) ([]TaskAction, []Artifact) {
var actions []TaskAction