feat: publish sanitized custom.97 source
This commit is contained in:
1022 files changed
+69903
-19207
No files matched your search
@@ -0,0 +1,63 @@
|
||||
package claude
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/relaykit/dto"
|
||||
relaymedia "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/media"
|
||||
)
|
||||
|
||||
// imageMediaTypes are the image formats Claude accepts.
|
||||
var imageMediaTypes = map[string]struct{}{
|
||||
"image/jpeg": {},
|
||||
"image/png": {},
|
||||
"image/gif": {},
|
||||
"image/webp": {},
|
||||
}
|
||||
|
||||
// MediaBlock builds the Claude content block for resolved base64 media: an
|
||||
// image in a format Claude accepts, a PDF document, or a plain-text document
|
||||
// for text files. declaredImage marks media sent as an image part, which
|
||||
// passes through when its MIME type is unknown. A non-empty reason means
|
||||
// Claude cannot carry the media and the block must be omitted.
|
||||
func MediaBlock(base64Data string, mimeType string, declaredImage bool) (dto.ClaudeMediaMessage, string) {
|
||||
mediaType, _, _ := strings.Cut(strings.ToLower(strings.TrimSpace(mimeType)), ";")
|
||||
mediaType = strings.TrimSpace(mediaType)
|
||||
if mediaType == "image/jpg" {
|
||||
mediaType = "image/jpeg"
|
||||
}
|
||||
switch {
|
||||
case mediaType == "application/pdf":
|
||||
return dto.ClaudeMediaMessage{
|
||||
Type: "document",
|
||||
Source: &dto.ClaudeMessageSource{Type: "base64", MediaType: mediaType, Data: base64Data},
|
||||
}, ""
|
||||
case relaymedia.IsTextMimeType(mediaType):
|
||||
text, ok := relaymedia.DecodeText(base64Data)
|
||||
if !ok {
|
||||
return dto.ClaudeMediaMessage{}, fmt.Sprintf("text file of MIME type %q could not be decoded as UTF-8 text", mimeType)
|
||||
}
|
||||
return dto.ClaudeMediaMessage{
|
||||
Type: "document",
|
||||
Source: &dto.ClaudeMessageSource{Type: "text", MediaType: "text/plain", Data: text},
|
||||
}, ""
|
||||
case mediaType == "" && declaredImage:
|
||||
return dto.ClaudeMediaMessage{
|
||||
Type: "image",
|
||||
Source: &dto.ClaudeMessageSource{Type: "base64", MediaType: mimeType, Data: base64Data},
|
||||
}, ""
|
||||
case mediaType == "application/octet-stream" && declaredImage:
|
||||
return dto.ClaudeMediaMessage{}, "the image format could not be identified"
|
||||
}
|
||||
if _, ok := imageMediaTypes[mediaType]; ok {
|
||||
return dto.ClaudeMediaMessage{
|
||||
Type: "image",
|
||||
Source: &dto.ClaudeMessageSource{Type: "base64", MediaType: mediaType, Data: base64Data},
|
||||
}, ""
|
||||
}
|
||||
if strings.HasPrefix(mediaType, "image/") {
|
||||
return dto.ClaudeMediaMessage{}, fmt.Sprintf("Claude accepts only JPEG, PNG, GIF, and WebP images, not %q", mimeType)
|
||||
}
|
||||
return dto.ClaudeMediaMessage{}, fmt.Sprintf("Claude Messages cannot carry content of MIME type %q", mimeType)
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func ApplyReasoning(ctx context.Context, req *dto.ClaudeRequest, info convmeta.M
|
||||
case req.Thinking.BudgetTokens != nil:
|
||||
effort = string(reasoning.EffortFromBudget(*req.Thinking.BudgetTokens))
|
||||
case req.Thinking.Type == "enabled" || req.Thinking.Type == "adaptive":
|
||||
effort = string(reasoning.EffortHigh)
|
||||
effort = string(reasoning.ClaudeDefaultEffort(req.Model))
|
||||
}
|
||||
}
|
||||
info.SetReasoningEffort(effort)
|
||||
@@ -81,6 +81,16 @@ func ApplyReasoning(ctx context.Context, req *dto.ClaudeRequest, info convmeta.M
|
||||
intent.Effort = reasoning.EffortHigh
|
||||
}
|
||||
}
|
||||
requestEffort, err := reasoning.ParseEffort(req.GetEfforts())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if knownClaudeModel && intent.Mode == reasoning.ModeDisabled && (intent.Effort == "" || intent.Effort == reasoning.EffortNone) &&
|
||||
requestEffort != "" && requestEffort != reasoning.EffortNone {
|
||||
// Effort works without thinking, so the request's own effort is
|
||||
// rendered for the thinking-off state rather than left unchecked.
|
||||
intent.Effort = requestEffort
|
||||
}
|
||||
if req.MaxTokens == nil && intent.HasStrength() {
|
||||
// Adapter-provided defaults may be raised to accommodate an exact
|
||||
// cross-protocol budget. Values too small for manual thinking are
|
||||
@@ -117,7 +127,14 @@ func ApplyReasoning(ctx context.Context, req *dto.ClaudeRequest, info convmeta.M
|
||||
if rendered.Thinking != nil {
|
||||
req.Thinking = rendered.Thinking
|
||||
}
|
||||
if rendered.OutputEffort != "" {
|
||||
outputEffort := rendered.OutputEffort
|
||||
// An effort the request still carries reaches a known model only at a
|
||||
// level it accepts, and is removed where the model takes none.
|
||||
rewriteRequestEffort := outputEffort == "" && knownClaudeModel && requestEffort != ""
|
||||
if rewriteRequestEffort {
|
||||
outputEffort = reasoning.ClaudeEffort(capabilityModel, requestEffort)
|
||||
}
|
||||
if outputEffort != "" || rewriteRequestEffort {
|
||||
outputConfig := make(map[string]any)
|
||||
if len(req.OutputConfig) > 0 {
|
||||
if kitutil.GetJsonType(req.OutputConfig) != "object" {
|
||||
@@ -130,12 +147,20 @@ func ApplyReasoning(ctx context.Context, req *dto.ClaudeRequest, info convmeta.M
|
||||
outputConfig = make(map[string]any)
|
||||
}
|
||||
}
|
||||
outputConfig["effort"] = string(rendered.OutputEffort)
|
||||
encoded, err := kitutil.Marshal(outputConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal Claude output_config: %w", err)
|
||||
if outputEffort != "" {
|
||||
outputConfig["effort"] = string(outputEffort)
|
||||
} else {
|
||||
delete(outputConfig, "effort")
|
||||
}
|
||||
if len(outputConfig) == 0 {
|
||||
req.OutputConfig = nil
|
||||
} else {
|
||||
encoded, err := kitutil.Marshal(outputConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal Claude output_config: %w", err)
|
||||
}
|
||||
req.OutputConfig = encoded
|
||||
}
|
||||
req.OutputConfig = encoded
|
||||
}
|
||||
if rendered.ClearSampling {
|
||||
if req.Temperature != nil || req.TopP != nil || req.TopK != nil {
|
||||
|
||||
Reference in new issue
Block a user