// backend, in canonical form.
quotaPrefix = "X-Codex-"
+ // turnStateHeader is an x-codex-* header that holds an opaque state of
+ // the backend, not quota.
+ turnStateHeader = "X-Codex-Turn-State"
+
// errorBytes is the most bytes of an error response that go into the
// error.
errorBytes = 4096
// Client sends model calls to a Responses endpoint for one agent. The ChatGPT
// backend and the OpenAI API take the same request body and differ only in the
// URL and the credentials. Every call sends the fixed cache key of the agent,
-// so the turns and the compactions of the agent share one prompt cache. Every
-// call is recorded in the usage file.
+// so the turns and the compactions of the agent share one prompt cache. The
+// ChatGPT backend caches only with the key in the session_id header, so a call
+// to it sends the key there and in the body. Every call is recorded in the
+// usage file.
//
// The request never holds max_output_tokens or metadata, because the ChatGPT
// backend refuses them. It sets store to false, so the client sends the whole
cacheKey string
usage *usageLog
http *http.Client
- clock func() time.Time
}
// Request is one model call. Input holds the input items in order, such as
usage: &usageLog{
path: usagePath,
},
- http: &http.Client{},
- clock: time.Now,
+ http: &http.Client{},
}
}
return Response{}, fmt.Errorf("authorize model call: %w", err)
}
+ _, chatgpt := c.auth.(*chatgptAuth)
+ if chatgpt {
+ httpRequest.Header.Set("session_id", c.cacheKey)
+ }
+
record := usageRecord{
- Date: c.clock().Format(time.RFC3339Nano),
+ Date: time.Now().Format(time.RFC3339Nano),
Model: request.Model,
}
}
// usageRecord is one line of usage.jsonl. Status is the HTTP status, or 0 when
-// no response came. Quota holds the x-codex-* headers of the response.
+// no response came. Quota holds the x-codex-* headers of the response, without
+// the turn state.
type usageRecord struct {
Date string `json:"date"`
Model string `json:"model"`
}
}
-// quotaHeaders returns the x-codex-* headers, with lowercase names.
+// quotaHeaders returns the x-codex-* headers, with lowercase names, without the
+// turn state.
func quotaHeaders(header http.Header) map[string]string {
var quota map[string]string
for name, values := range header {
- if !strings.HasPrefix(name, quotaPrefix) || len(values) == 0 {
+ if !strings.HasPrefix(name, quotaPrefix) || name == turnStateHeader || len(values) == 0 {
continue
}