first commit
This commit is contained in:
359
core/providers/anthropic/batch.go
Normal file
359
core/providers/anthropic/batch.go
Normal file
@@ -0,0 +1,359 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user