// 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.
-// 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.
+// A call of a subagent sends the key of the agent with "-" and the name of the
+// subagent, so that the calls of a subagent do not share a cache entry with the
+// calls of the agent that run at the same time. 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
return ModelResponse{}, errModelCallLimit
}
+ cacheKey := c.cacheKey
+ if request.Agent != "" {
+ cacheKey += "-" + request.Agent
+ }
+
body, err := json.Marshal(ModelRequestBody{
Model: request.Model,
Instructions: request.Instructions,
Store: false,
Stream: true,
Include: []string{"reasoning.encrypted_content"},
- PromptCacheKey: c.cacheKey,
+ PromptCacheKey: cacheKey,
})
if err != nil {
return ModelResponse{}, fmt.Errorf("encode request: %w", err)
_, chatgpt := c.auth.(*ChatGPTAuth)
if chatgpt {
- httpRequest.Header.Set("session_id", c.cacheKey)
+ httpRequest.Header.Set("session_id", cacheKey)
}
start := time.Now()
// modelUserMessage values or output items of an earlier response. Node names
// the node that a compaction builds, or is nil. Turn names the request of a
// turn: first or next. Agent names the subagent that makes the call, or is
-// empty for a call of the agent itself. Node, Turn, and Agent go only to the
-// usage file, never to the endpoint.
+// empty for a call of the agent itself. Node, Turn, and Agent go to the usage
+// file, never to the endpoint as fields. Agent also gives the call the cache
+// key of its subagent.
type ModelRequest struct {
Model string
Effort string
call := server.Call(0)
require.Equal(t, tc.want, call.header.Get("session_id"))
require.Equal(t, "agent-key", call.body["prompt_cache_key"])
+
+ // A subagent has its own key.
+ request := modelTestRequest()
+ request.Agent = "GreenOtter"
+ _, err = client.Create(t.Context(), request)
+ require.NoError(t, err)
+
+ call = server.Call(1)
+ if tc.want != "" {
+ require.Equal(t, "agent-key-GreenOtter", call.header.Get("session_id"))
+ }
+
+ require.Equal(t, "agent-key-GreenOtter", call.body["prompt_cache_key"])
})
}
}
require.NoError(t, err)
require.NotContains(t, server.Call(0).body, "node")
require.NotContains(t, string(server.Call(0).raw), "[2,5]")
- require.NotContains(t, string(server.Call(0).raw), "GreenOtter")
+ require.NotContains(t, server.Call(0).body, "agent")
_, err = client.Create(t.Context(), modelTestRequest())
require.ErrorContains(t, err, "HTTP 429")