360 lines
12 KiB
Go
360 lines
12 KiB
Go
package anthropic
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/maximhq/bifrost/core/schemas"
|
|
)
|
|
|
|
// Anthropic Batch API Types
|
|
|
|
// AnthropicBatchRequestItem represents a single request in a batch.
|
|
type AnthropicBatchRequestItem struct {
|
|
CustomID string `json:"custom_id"`
|
|
Params map[string]any `json:"params"`
|
|
}
|
|
|
|
// AnthropicBatchCreateRequest represents the request body for creating a batch.
|
|
type AnthropicBatchCreateRequest struct {
|
|
Requests []AnthropicBatchRequestItem `json:"requests"`
|
|
}
|
|
|
|
// AnthropicBatchCancelRequest represents the request body for canceling a batch.
|
|
type AnthropicBatchCancelRequest struct {
|
|
BatchID string `json:"batch_id"`
|
|
}
|
|
|
|
// AnthropicBatchRetrieveRequest represents the request body for retrieving a batch.
|
|
type AnthropicBatchRetrieveRequest struct {
|
|
BatchID string `json:"batch_id"`
|
|
}
|
|
|
|
// AnthropicBatchListRequest represents the request body for listing batches.
|
|
type AnthropicBatchListRequest struct {
|
|
PageToken *string `json:"page_token"`
|
|
PageSize int `json:"page_size"`
|
|
}
|
|
|
|
// AnthropicBatchResultsRequest represents the request body for retrieving batch results.
|
|
type AnthropicBatchResultsRequest struct {
|
|
BatchID string `json:"batch_id"`
|
|
}
|
|
|
|
// AnthropicBatchResponse represents an Anthropic batch response.
|
|
type AnthropicBatchResponse struct {
|
|
ID string `json:"id"`
|
|
Type string `json:"type"`
|
|
ProcessingStatus string `json:"processing_status"`
|
|
RequestCounts *AnthropicBatchRequestCounts `json:"request_counts,omitempty"`
|
|
EndedAt *string `json:"ended_at,omitempty"`
|
|
CreatedAt string `json:"created_at"`
|
|
ExpiresAt string `json:"expires_at"`
|
|
ArchivedAt *string `json:"archived_at,omitempty"`
|
|
CancelInitiatedAt *string `json:"cancel_initiated_at,omitempty"`
|
|
ResultsURL *string `json:"results_url,omitempty"`
|
|
}
|
|
|
|
// AnthropicBatchRequestCounts represents the request counts for a batch.
|
|
type AnthropicBatchRequestCounts struct {
|
|
Processing int `json:"processing"`
|
|
Succeeded int `json:"succeeded"`
|
|
Errored int `json:"errored"`
|
|
Canceled int `json:"canceled"`
|
|
Expired int `json:"expired"`
|
|
}
|
|
|
|
// AnthropicBatchListResponse represents the response from listing batches.
|
|
type AnthropicBatchListResponse struct {
|
|
Data []AnthropicBatchResponse `json:"data"`
|
|
HasMore bool `json:"has_more"`
|
|
FirstID *string `json:"first_id,omitempty"`
|
|
LastID *string `json:"last_id,omitempty"`
|
|
}
|
|
|
|
// AnthropicBatchResultItem represents a single result from a batch.
|
|
type AnthropicBatchResultItem struct {
|
|
CustomID string `json:"custom_id"`
|
|
Result AnthropicBatchResultData `json:"result"`
|
|
}
|
|
|
|
// AnthropicBatchResultData represents the result data.
|
|
type AnthropicBatchResultData struct {
|
|
Type string `json:"type"` // "succeeded", "errored", "expired", "canceled"
|
|
Message map[string]interface{} `json:"message,omitempty"`
|
|
Error *AnthropicBatchError `json:"error,omitempty"`
|
|
}
|
|
|
|
// AnthropicBatchError represents an error in batch results.
|
|
type AnthropicBatchError struct {
|
|
Type string `json:"type"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// ToBifrostBatchStatus converts Anthropic processing_status to Bifrost status.
|
|
func ToBifrostBatchStatus(status string) schemas.BatchStatus {
|
|
switch status {
|
|
case "in_progress":
|
|
return schemas.BatchStatusInProgress
|
|
case "canceling":
|
|
return schemas.BatchStatusCancelling
|
|
case "ended":
|
|
return schemas.BatchStatusEnded
|
|
default:
|
|
return schemas.BatchStatus(status)
|
|
}
|
|
}
|
|
|
|
// parseAnthropicTimestamp converts Anthropic ISO timestamp to Unix timestamp.
|
|
func parseAnthropicTimestamp(timestamp string) int64 {
|
|
if timestamp == "" {
|
|
return 0
|
|
}
|
|
t, err := time.Parse(time.RFC3339Nano, timestamp)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return t.Unix()
|
|
}
|
|
|
|
// ToBifrostObjectType converts Anthropic type to Bifrost object type.
|
|
func ToBifrostObjectType(anthropicType string) string {
|
|
switch anthropicType {
|
|
case "message_batch":
|
|
return "batch"
|
|
default:
|
|
return anthropicType
|
|
}
|
|
}
|
|
|
|
// ToBifrostBatchCreateResponse converts Anthropic batch response to Bifrost batch create response.
|
|
func (r *AnthropicBatchResponse) ToBifrostBatchCreateResponse(latency time.Duration, sendBackRawRequest bool, sendBackRawResponse bool, rawRequest interface{}, rawResponse interface{}) *schemas.BifrostBatchCreateResponse {
|
|
expiresAt := parseAnthropicTimestamp(r.ExpiresAt)
|
|
resp := &schemas.BifrostBatchCreateResponse{
|
|
ID: r.ID,
|
|
Object: ToBifrostObjectType(r.Type),
|
|
Status: ToBifrostBatchStatus(r.ProcessingStatus),
|
|
ProcessingStatus: &r.ProcessingStatus,
|
|
ResultsURL: r.ResultsURL,
|
|
CreatedAt: parseAnthropicTimestamp(r.CreatedAt),
|
|
ExpiresAt: &expiresAt,
|
|
ExtraFields: schemas.BifrostResponseExtraFields{
|
|
Latency: latency.Milliseconds(),
|
|
},
|
|
}
|
|
|
|
if r.RequestCounts != nil {
|
|
resp.RequestCounts = schemas.BatchRequestCounts{
|
|
Total: r.RequestCounts.Processing + r.RequestCounts.Succeeded + r.RequestCounts.Errored + r.RequestCounts.Canceled + r.RequestCounts.Expired,
|
|
Completed: r.RequestCounts.Succeeded,
|
|
Failed: r.RequestCounts.Errored,
|
|
Succeeded: r.RequestCounts.Succeeded,
|
|
Expired: r.RequestCounts.Expired,
|
|
Canceled: r.RequestCounts.Canceled,
|
|
Pending: r.RequestCounts.Processing,
|
|
}
|
|
}
|
|
|
|
if sendBackRawRequest {
|
|
resp.ExtraFields.RawRequest = rawRequest
|
|
}
|
|
|
|
if sendBackRawResponse {
|
|
resp.ExtraFields.RawResponse = rawResponse
|
|
}
|
|
|
|
return resp
|
|
}
|
|
|
|
// ToBifrostBatchRetrieveResponse converts Anthropic batch response to Bifrost batch retrieve response.
|
|
func (r *AnthropicBatchResponse) ToBifrostBatchRetrieveResponse(latency time.Duration, sendBackRawRequest bool, sendBackRawResponse bool, rawRequest interface{}, rawResponse interface{}) *schemas.BifrostBatchRetrieveResponse {
|
|
resp := &schemas.BifrostBatchRetrieveResponse{
|
|
ID: r.ID,
|
|
Object: ToBifrostObjectType(r.Type),
|
|
Status: ToBifrostBatchStatus(r.ProcessingStatus),
|
|
ProcessingStatus: &r.ProcessingStatus,
|
|
ResultsURL: r.ResultsURL,
|
|
CreatedAt: parseAnthropicTimestamp(r.CreatedAt),
|
|
ExtraFields: schemas.BifrostResponseExtraFields{
|
|
Latency: latency.Milliseconds(),
|
|
},
|
|
}
|
|
|
|
if sendBackRawRequest {
|
|
resp.ExtraFields.RawRequest = rawRequest
|
|
}
|
|
|
|
expiresAt := parseAnthropicTimestamp(r.ExpiresAt)
|
|
if expiresAt > 0 {
|
|
resp.ExpiresAt = &expiresAt
|
|
}
|
|
|
|
if r.EndedAt != nil {
|
|
endedAt := parseAnthropicTimestamp(*r.EndedAt)
|
|
resp.CompletedAt = &endedAt
|
|
}
|
|
|
|
if r.ArchivedAt != nil {
|
|
archivedAt := parseAnthropicTimestamp(*r.ArchivedAt)
|
|
resp.ArchivedAt = &archivedAt
|
|
}
|
|
|
|
if r.CancelInitiatedAt != nil {
|
|
cancellingAt := parseAnthropicTimestamp(*r.CancelInitiatedAt)
|
|
resp.CancellingAt = &cancellingAt
|
|
}
|
|
|
|
if r.RequestCounts != nil {
|
|
resp.RequestCounts = schemas.BatchRequestCounts{
|
|
Total: r.RequestCounts.Processing + r.RequestCounts.Succeeded + r.RequestCounts.Errored + r.RequestCounts.Canceled + r.RequestCounts.Expired,
|
|
Completed: r.RequestCounts.Succeeded,
|
|
Failed: r.RequestCounts.Errored,
|
|
Succeeded: r.RequestCounts.Succeeded,
|
|
Expired: r.RequestCounts.Expired,
|
|
Canceled: r.RequestCounts.Canceled,
|
|
Pending: r.RequestCounts.Processing,
|
|
}
|
|
}
|
|
|
|
if sendBackRawResponse {
|
|
resp.ExtraFields.RawResponse = rawResponse
|
|
}
|
|
|
|
return resp
|
|
}
|
|
|
|
// ToAnthropicBatchCreateResponse converts a Bifrost batch create response to Anthropic format.
|
|
func ToAnthropicBatchCreateResponse(resp *schemas.BifrostBatchCreateResponse) *AnthropicBatchResponse {
|
|
result := &AnthropicBatchResponse{
|
|
ID: resp.ID,
|
|
Type: "message_batch",
|
|
ProcessingStatus: toAnthropicProcessingStatus(resp.Status),
|
|
CreatedAt: formatAnthropicTimestamp(resp.CreatedAt),
|
|
ResultsURL: resp.ResultsURL,
|
|
}
|
|
if resp.ExpiresAt != nil {
|
|
result.ExpiresAt = formatAnthropicTimestamp(*resp.ExpiresAt)
|
|
} else {
|
|
// This is a fallback for worst case scenario where expires_at is not available
|
|
// Which is never expected to happen, but just in case.
|
|
result.ExpiresAt = formatAnthropicTimestamp(time.Now().Add(24 * time.Hour).Unix())
|
|
}
|
|
if resp.RequestCounts.Total > 0 {
|
|
result.RequestCounts = &AnthropicBatchRequestCounts{
|
|
Processing: resp.RequestCounts.Pending,
|
|
Succeeded: resp.RequestCounts.Succeeded,
|
|
Errored: resp.RequestCounts.Failed,
|
|
Canceled: resp.RequestCounts.Canceled,
|
|
Expired: resp.RequestCounts.Expired,
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// ToAnthropicBatchListResponse converts a Bifrost batch list response to Anthropic format.
|
|
func ToAnthropicBatchListResponse(resp *schemas.BifrostBatchListResponse) *AnthropicBatchListResponse {
|
|
result := &AnthropicBatchListResponse{
|
|
Data: make([]AnthropicBatchResponse, len(resp.Data)),
|
|
HasMore: resp.HasMore,
|
|
FirstID: resp.FirstID,
|
|
LastID: resp.LastID,
|
|
}
|
|
|
|
for i, batch := range resp.Data {
|
|
result.Data[i] = *ToAnthropicBatchRetrieveResponse(&batch)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// ToAnthropicBatchRetrieveResponse converts a Bifrost batch retrieve response to Anthropic format.
|
|
func ToAnthropicBatchRetrieveResponse(resp *schemas.BifrostBatchRetrieveResponse) *AnthropicBatchResponse {
|
|
result := &AnthropicBatchResponse{
|
|
ID: resp.ID,
|
|
Type: "message_batch",
|
|
ProcessingStatus: toAnthropicProcessingStatus(resp.Status),
|
|
CreatedAt: formatAnthropicTimestamp(resp.CreatedAt),
|
|
ResultsURL: resp.ResultsURL,
|
|
}
|
|
|
|
if resp.ExpiresAt != nil {
|
|
result.ExpiresAt = formatAnthropicTimestamp(*resp.ExpiresAt)
|
|
}
|
|
|
|
if resp.CompletedAt != nil {
|
|
endedAt := formatAnthropicTimestamp(*resp.CompletedAt)
|
|
result.EndedAt = &endedAt
|
|
}
|
|
|
|
if resp.ArchivedAt != nil {
|
|
archivedAt := formatAnthropicTimestamp(*resp.ArchivedAt)
|
|
result.ArchivedAt = &archivedAt
|
|
}
|
|
|
|
if resp.CancellingAt != nil {
|
|
cancelInitiatedAt := formatAnthropicTimestamp(*resp.CancellingAt)
|
|
result.CancelInitiatedAt = &cancelInitiatedAt
|
|
}
|
|
|
|
if resp.RequestCounts.Total > 0 {
|
|
result.RequestCounts = &AnthropicBatchRequestCounts{
|
|
Processing: resp.RequestCounts.Pending,
|
|
Succeeded: resp.RequestCounts.Succeeded,
|
|
Errored: resp.RequestCounts.Failed,
|
|
Canceled: resp.RequestCounts.Canceled,
|
|
Expired: resp.RequestCounts.Expired,
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// ToAnthropicBatchCancelResponse converts a Bifrost batch cancel response to Anthropic format.
|
|
func ToAnthropicBatchCancelResponse(resp *schemas.BifrostBatchCancelResponse) *AnthropicBatchResponse {
|
|
result := &AnthropicBatchResponse{
|
|
ID: resp.ID,
|
|
Type: "message_batch",
|
|
ProcessingStatus: toAnthropicProcessingStatus(resp.Status),
|
|
}
|
|
|
|
if resp.CancellingAt != nil {
|
|
cancelInitiatedAt := formatAnthropicTimestamp(*resp.CancellingAt)
|
|
result.CancelInitiatedAt = &cancelInitiatedAt
|
|
}
|
|
|
|
if resp.RequestCounts.Total > 0 {
|
|
result.RequestCounts = &AnthropicBatchRequestCounts{
|
|
Processing: resp.RequestCounts.Pending,
|
|
Succeeded: resp.RequestCounts.Succeeded,
|
|
Canceled: resp.RequestCounts.Canceled,
|
|
Expired: resp.RequestCounts.Expired,
|
|
Errored: resp.RequestCounts.Failed,
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// toAnthropicProcessingStatus converts Bifrost batch status to Anthropic processing_status.
|
|
func toAnthropicProcessingStatus(status schemas.BatchStatus) string {
|
|
switch status {
|
|
case schemas.BatchStatusInProgress:
|
|
fallthrough
|
|
case schemas.BatchStatusValidating:
|
|
return "in_progress"
|
|
case schemas.BatchStatusCancelling:
|
|
return "canceling"
|
|
case schemas.BatchStatusEnded, schemas.BatchStatusCompleted, schemas.BatchStatusCancelled:
|
|
return "ended"
|
|
default:
|
|
return string(status)
|
|
}
|
|
}
|
|
|
|
// formatAnthropicTimestamp converts Unix timestamp to Anthropic ISO timestamp format.
|
|
func formatAnthropicTimestamp(unixTime int64) string {
|
|
if unixTime == 0 {
|
|
return ""
|
|
}
|
|
return time.Unix(unixTime, 0).UTC().Format(time.RFC3339)
|
|
}
|