fix(codex): include required instructions and improve account-id extraction
This commit is contained in:
@@ -359,6 +359,9 @@ func parseTokenResponse(body []byte, provider string) (*AuthCredential, error) {
|
|||||||
|
|
||||||
if accountID := extractAccountID(tokenResp.AccessToken); accountID != "" {
|
if accountID := extractAccountID(tokenResp.AccessToken); accountID != "" {
|
||||||
cred.AccountID = accountID
|
cred.AccountID = accountID
|
||||||
|
} else if accountID := extractAccountID(tokenResp.IDToken); accountID != "" {
|
||||||
|
// Recent OpenAI OAuth responses may only include chatgpt_account_id in id_token claims.
|
||||||
|
cred.AccountID = accountID
|
||||||
}
|
}
|
||||||
|
|
||||||
return cred, nil
|
return cred, nil
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
@@ -91,6 +92,32 @@ func TestParseTokenResponseNoAccessToken(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseTokenResponseAccountIDFromIDToken(t *testing.T) {
|
||||||
|
idToken := makeJWTWithAccountID("acc-from-id")
|
||||||
|
resp := map[string]interface{}{
|
||||||
|
"access_token": "not-a-jwt",
|
||||||
|
"refresh_token": "test-refresh-token",
|
||||||
|
"expires_in": 3600,
|
||||||
|
"id_token": idToken,
|
||||||
|
}
|
||||||
|
body, _ := json.Marshal(resp)
|
||||||
|
|
||||||
|
cred, err := parseTokenResponse(body, "openai")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("parseTokenResponse() error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cred.AccountID != "acc-from-id" {
|
||||||
|
t.Errorf("AccountID = %q, want %q", cred.AccountID, "acc-from-id")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeJWTWithAccountID(accountID string) string {
|
||||||
|
header := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"none","typ":"JWT"}`))
|
||||||
|
payload := base64.RawURLEncoding.EncodeToString([]byte(`{"https://api.openai.com/auth":{"chatgpt_account_id":"` + accountID + `"}}`))
|
||||||
|
return header + "." + payload + ".sig"
|
||||||
|
}
|
||||||
|
|
||||||
func TestExchangeCodeForTokens(t *testing.T) {
|
func TestExchangeCodeForTokens(t *testing.T) {
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.URL.Path != "/oauth/token" {
|
if r.URL.Path != "/oauth/token" {
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ type CodexProvider struct {
|
|||||||
tokenSource func() (string, string, error)
|
tokenSource func() (string, string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultCodexInstructions = "You are Codex, a coding assistant."
|
||||||
|
|
||||||
func NewCodexProvider(token, accountID string) *CodexProvider {
|
func NewCodexProvider(token, accountID string) *CodexProvider {
|
||||||
opts := []option.RequestOption{
|
opts := []option.RequestOption{
|
||||||
option.WithBaseURL("https://chatgpt.com/backend-api/codex"),
|
option.WithBaseURL("https://chatgpt.com/backend-api/codex"),
|
||||||
@@ -138,6 +140,9 @@ func buildCodexParams(messages []Message, tools []ToolDefinition, model string,
|
|||||||
|
|
||||||
if instructions != "" {
|
if instructions != "" {
|
||||||
params.Instructions = openai.Opt(instructions)
|
params.Instructions = openai.Opt(instructions)
|
||||||
|
} else {
|
||||||
|
// ChatGPT Codex backend requires instructions to be present.
|
||||||
|
params.Instructions = openai.Opt(defaultCodexInstructions)
|
||||||
}
|
}
|
||||||
|
|
||||||
if maxTokens, ok := options["max_tokens"].(int); ok {
|
if maxTokens, ok := options["max_tokens"].(int); ok {
|
||||||
|
|||||||
@@ -21,6 +21,12 @@ func TestBuildCodexParams_BasicMessage(t *testing.T) {
|
|||||||
if params.Model != "gpt-4o" {
|
if params.Model != "gpt-4o" {
|
||||||
t.Errorf("Model = %q, want %q", params.Model, "gpt-4o")
|
t.Errorf("Model = %q, want %q", params.Model, "gpt-4o")
|
||||||
}
|
}
|
||||||
|
if !params.Instructions.Valid() {
|
||||||
|
t.Fatal("Instructions should be set")
|
||||||
|
}
|
||||||
|
if params.Instructions.Or("") != defaultCodexInstructions {
|
||||||
|
t.Errorf("Instructions = %q, want %q", params.Instructions.Or(""), defaultCodexInstructions)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildCodexParams_SystemAsInstructions(t *testing.T) {
|
func TestBuildCodexParams_SystemAsInstructions(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user