removing restcting of available models, fixing max tokens issues in test

This commit is contained in:
Badri Narayanan S
2025-12-27 12:17:45 +05:30
parent f86c4f4d32
commit 9b7dcf3a6c
10 changed files with 63 additions and 93 deletions

View File

@@ -13,13 +13,11 @@ import crypto from 'crypto';
import { import {
ANTIGRAVITY_ENDPOINT_FALLBACKS, ANTIGRAVITY_ENDPOINT_FALLBACKS,
ANTIGRAVITY_HEADERS, ANTIGRAVITY_HEADERS,
AVAILABLE_MODELS,
MAX_RETRIES, MAX_RETRIES,
MAX_WAIT_BEFORE_ERROR_MS, MAX_WAIT_BEFORE_ERROR_MS,
MIN_SIGNATURE_LENGTH MIN_SIGNATURE_LENGTH
} from './constants.js'; } from './constants.js';
import { import {
mapModelName,
convertAnthropicToGoogle, convertAnthropicToGoogle,
convertGoogleToAnthropic convertGoogleToAnthropic
} from './format-converter.js'; } from './format-converter.js';
@@ -219,7 +217,7 @@ function parseResetTime(responseOrError, errorText = '') {
* Build the wrapped request body for Cloud Code API * Build the wrapped request body for Cloud Code API
*/ */
function buildCloudCodeRequest(anthropicRequest, projectId) { function buildCloudCodeRequest(anthropicRequest, projectId) {
const model = mapModelName(anthropicRequest.model); const model = anthropicRequest.model;
const googleRequest = convertAnthropicToGoogle(anthropicRequest); const googleRequest = convertAnthropicToGoogle(anthropicRequest);
// Use stable session ID derived from first user message for cache continuity // Use stable session ID derived from first user message for cache continuity
@@ -247,7 +245,7 @@ function buildHeaders(token, model, accept = 'application/json') {
}; };
// Add interleaved thinking header for Claude thinking models // Add interleaved thinking header for Claude thinking models
const isThinkingModel = model.toLowerCase().includes('thinking'); const isThinkingModel = model.toLowerCase().includes('claude') && model.toLowerCase().includes('thinking');
if (isThinkingModel) { if (isThinkingModel) {
headers['anthropic-beta'] = 'interleaved-thinking-2025-05-14'; headers['anthropic-beta'] = 'interleaved-thinking-2025-05-14';
} }
@@ -273,8 +271,8 @@ function buildHeaders(token, model, accept = 'application/json') {
* @throws {Error} If max retries exceeded or no accounts available * @throws {Error} If max retries exceeded or no accounts available
*/ */
export async function sendMessage(anthropicRequest, accountManager) { export async function sendMessage(anthropicRequest, accountManager) {
const model = mapModelName(anthropicRequest.model); const model = anthropicRequest.model;
const isThinkingModel = model.toLowerCase().includes('thinking'); const isThinkingModel = model.toLowerCase().includes('claude') && model.toLowerCase().includes('thinking');
// Retry loop with account failover // Retry loop with account failover
// Ensure we try at least as many times as there are accounts to cycle through everyone // Ensure we try at least as many times as there are accounts to cycle through everyone
@@ -537,7 +535,7 @@ async function parseThinkingSSEResponse(response, originalModel) {
* @throws {Error} If max retries exceeded or no accounts available * @throws {Error} If max retries exceeded or no accounts available
*/ */
export async function* sendMessageStream(anthropicRequest, accountManager) { export async function* sendMessageStream(anthropicRequest, accountManager) {
const model = mapModelName(anthropicRequest.model); const model = anthropicRequest.model;
// Retry loop with account failover // Retry loop with account failover
// Ensure we try at least as many times as there are accounts to cycle through everyone // Ensure we try at least as many times as there are accounts to cycle through everyone
@@ -931,19 +929,28 @@ async function* streamSSEResponse(response, originalModel) {
/** /**
* List available models in Anthropic API format * List available models in Anthropic API format
* Fetches models dynamically from the Cloud Code API
* *
* @returns {{object: string, data: Array<{id: string, object: string, created: number, owned_by: string, description: string}>}} List of available models * @param {string} token - OAuth access token
* @returns {Promise<{object: string, data: Array<{id: string, object: string, created: number, owned_by: string, description: string}>}>} List of available models
*/ */
export function listModels() { export async function listModels(token) {
const data = await fetchAvailableModels(token);
if (!data || !data.models) {
return { object: 'list', data: [] };
}
const modelList = Object.entries(data.models).map(([modelId, modelData]) => ({
id: modelId,
object: 'model',
created: Math.floor(Date.now() / 1000),
owned_by: 'anthropic',
description: modelData.displayName || modelId
}));
return { return {
object: 'list', object: 'list',
data: AVAILABLE_MODELS.map(m => ({ data: modelList
id: m.id,
object: 'model',
created: Math.floor(Date.now() / 1000),
owned_by: 'anthropic',
description: m.description
}))
}; };
} }

View File

@@ -56,44 +56,6 @@ export const ANTIGRAVITY_HEADERS = {
}) })
}; };
// Model name mappings: Anthropic format → Antigravity format
export const MODEL_MAPPINGS = {
// Claude models
'claude-3-opus-20240229': 'claude-opus-4-5-thinking',
'claude-3-5-opus-20240229': 'claude-opus-4-5-thinking',
'claude-3-5-sonnet-20241022': 'claude-sonnet-4-5',
'claude-3-5-sonnet-20240620': 'claude-sonnet-4-5',
'claude-3-sonnet-20240229': 'claude-sonnet-4-5',
'claude-sonnet-4-5': 'claude-sonnet-4-5',
'claude-sonnet-4-5-thinking': 'claude-sonnet-4-5-thinking',
'claude-opus-4-5-thinking': 'claude-opus-4-5-thinking'
};
// Available models exposed by this proxy
export const AVAILABLE_MODELS = [
{
id: 'claude-sonnet-4-5',
name: 'Claude Sonnet 4.5 (Antigravity)',
description: 'Claude Sonnet 4.5 via Antigravity Cloud Code',
context: 200000,
output: 64000
},
{
id: 'claude-sonnet-4-5-thinking',
name: 'Claude Sonnet 4.5 Thinking (Antigravity)',
description: 'Claude Sonnet 4.5 with extended thinking via Antigravity',
context: 200000,
output: 64000
},
{
id: 'claude-opus-4-5-thinking',
name: 'Claude Opus 4.5 Thinking (Antigravity)',
description: 'Claude Opus 4.5 with extended thinking via Antigravity',
context: 200000,
output: 64000
}
];
// Default project ID if none can be discovered // Default project ID if none can be discovered
export const DEFAULT_PROJECT_ID = 'rising-fact-p41fc'; export const DEFAULT_PROJECT_ID = 'rising-fact-p41fc';
@@ -120,7 +82,6 @@ export const MAX_ACCOUNTS = 10; // Maximum number of accounts allowed
export const MAX_WAIT_BEFORE_ERROR_MS = 120000; // 2 minutes - throw error if wait exceeds this export const MAX_WAIT_BEFORE_ERROR_MS = 120000; // 2 minutes - throw error if wait exceeds this
// Thinking model constants // Thinking model constants
export const CLAUDE_THINKING_MAX_OUTPUT_TOKENS = 64000; // Max output tokens for thinking models
export const MIN_SIGNATURE_LENGTH = 50; // Minimum valid thinking signature length export const MIN_SIGNATURE_LENGTH = 50; // Minimum valid thinking signature length
// Google OAuth configuration (from opencode-antigravity-auth) // Google OAuth configuration (from opencode-antigravity-auth)
@@ -144,8 +105,6 @@ export const OAUTH_REDIRECT_URI = `http://localhost:${OAUTH_CONFIG.callbackPort}
export default { export default {
ANTIGRAVITY_ENDPOINT_FALLBACKS, ANTIGRAVITY_ENDPOINT_FALLBACKS,
ANTIGRAVITY_HEADERS, ANTIGRAVITY_HEADERS,
MODEL_MAPPINGS,
AVAILABLE_MODELS,
DEFAULT_PROJECT_ID, DEFAULT_PROJECT_ID,
TOKEN_REFRESH_INTERVAL_MS, TOKEN_REFRESH_INTERVAL_MS,
REQUEST_BODY_LIMIT, REQUEST_BODY_LIMIT,
@@ -157,7 +116,6 @@ export default {
MAX_RETRIES, MAX_RETRIES,
MAX_ACCOUNTS, MAX_ACCOUNTS,
MAX_WAIT_BEFORE_ERROR_MS, MAX_WAIT_BEFORE_ERROR_MS,
CLAUDE_THINKING_MAX_OUTPUT_TOKENS,
MIN_SIGNATURE_LENGTH, MIN_SIGNATURE_LENGTH,
OAUTH_CONFIG, OAUTH_CONFIG,
OAUTH_REDIRECT_URI OAUTH_REDIRECT_URI

View File

@@ -9,20 +9,9 @@
import crypto from 'crypto'; import crypto from 'crypto';
import { import {
MODEL_MAPPINGS,
CLAUDE_THINKING_MAX_OUTPUT_TOKENS,
MIN_SIGNATURE_LENGTH MIN_SIGNATURE_LENGTH
} from './constants.js'; } from './constants.js';
/**
* Map Anthropic model name to Antigravity model name
* @param {string} anthropicModel - Anthropic format model name (e.g., 'claude-3-5-sonnet-20241022')
* @returns {string} Antigravity format model name (e.g., 'claude-sonnet-4-5')
*/
export function mapModelName(anthropicModel) {
return MODEL_MAPPINGS[anthropicModel] || anthropicModel;
}
/** /**
* Check if a part is a thinking block * Check if a part is a thinking block
* @param {Object} part - Content part to check * @param {Object} part - Content part to check
@@ -509,13 +498,6 @@ export function convertAnthropicToGoogle(anthropicRequest) {
const thinkingBudget = thinking?.budget_tokens; const thinkingBudget = thinking?.budget_tokens;
if (thinkingBudget) { if (thinkingBudget) {
thinkingConfig.thinking_budget = thinkingBudget; thinkingConfig.thinking_budget = thinkingBudget;
// Ensure maxOutputTokens is large enough when budget is specified
if (!googleRequest.generationConfig.maxOutputTokens ||
googleRequest.generationConfig.maxOutputTokens <= thinkingBudget) {
googleRequest.generationConfig.maxOutputTokens = CLAUDE_THINKING_MAX_OUTPUT_TOKENS;
}
console.log('[FormatConverter] Thinking enabled with budget:', thinkingBudget); console.log('[FormatConverter] Thinking enabled with budget:', thinkingBudget);
} else { } else {
console.log('[FormatConverter] Thinking enabled (no budget specified)'); console.log('[FormatConverter] Thinking enabled (no budget specified)');
@@ -725,7 +707,6 @@ export function convertGoogleToAnthropic(googleResponse, model) {
} }
export default { export default {
mapModelName,
convertAnthropicToGoogle, convertAnthropicToGoogle,
convertGoogleToAnthropic convertGoogleToAnthropic
}; };

View File

@@ -193,7 +193,7 @@ app.get('/account-limits', async (req, res) => {
} }
} }
const sortedModels = Array.from(allModelIds).filter(m => m.includes('claude')).sort(); const sortedModels = Array.from(allModelIds).sort();
// Return ASCII table format // Return ASCII table format
if (format === 'table') { if (format === 'table') {
@@ -352,8 +352,32 @@ app.post('/refresh-token', async (req, res) => {
/** /**
* List models endpoint (OpenAI-compatible format) * List models endpoint (OpenAI-compatible format)
*/ */
app.get('/v1/models', (req, res) => { app.get('/v1/models', async (req, res) => {
res.json(listModels()); try {
await ensureInitialized();
const account = accountManager.pickNext();
if (!account) {
return res.status(503).json({
type: 'error',
error: {
type: 'api_error',
message: 'No accounts available'
}
});
}
const token = await accountManager.getTokenForAccount(account);
const models = await listModels(token);
res.json(models);
} catch (error) {
console.error('[API] Error listing models:', error);
res.status(500).json({
type: 'error',
error: {
type: 'api_error',
message: error.message
}
});
}
}); });
/** /**

View File

@@ -36,7 +36,7 @@ async function runTests() {
const turn1 = await streamRequest({ const turn1 = await streamRequest({
model: 'claude-sonnet-4-5-thinking', model: 'claude-sonnet-4-5-thinking',
max_tokens: 2048, max_tokens: 16000,
stream: true, stream: true,
system: LARGE_SYSTEM_PROMPT, system: LARGE_SYSTEM_PROMPT,
thinking: { type: 'enabled', budget_tokens: 5000 }, thinking: { type: 'enabled', budget_tokens: 5000 },
@@ -90,7 +90,7 @@ async function runTests() {
const turn2 = await streamRequest({ const turn2 = await streamRequest({
model: 'claude-sonnet-4-5-thinking', model: 'claude-sonnet-4-5-thinking',
max_tokens: 2048, max_tokens: 16000,
stream: true, stream: true,
system: LARGE_SYSTEM_PROMPT, system: LARGE_SYSTEM_PROMPT,
thinking: { type: 'enabled', budget_tokens: 5000 }, thinking: { type: 'enabled', budget_tokens: 5000 },

View File

@@ -28,7 +28,7 @@ async function runTests() {
const result1 = await streamRequest({ const result1 = await streamRequest({
model: 'claude-sonnet-4-5-thinking', model: 'claude-sonnet-4-5-thinking',
max_tokens: 2048, max_tokens: 16000,
stream: true, stream: true,
thinking: { type: 'enabled', budget_tokens: 8000 }, thinking: { type: 'enabled', budget_tokens: 8000 },
messages: [{ messages: [{
@@ -79,7 +79,7 @@ async function runTests() {
const result2 = await streamRequest({ const result2 = await streamRequest({
model: 'claude-sonnet-4-5-thinking', model: 'claude-sonnet-4-5-thinking',
max_tokens: 2048, max_tokens: 16000,
stream: true, stream: true,
thinking: { type: 'enabled', budget_tokens: 8000 }, thinking: { type: 'enabled', budget_tokens: 8000 },
messages: [ messages: [

View File

@@ -30,7 +30,7 @@ async function runTests() {
const result = await streamRequest({ const result = await streamRequest({
model: 'claude-opus-4-5-thinking', model: 'claude-opus-4-5-thinking',
max_tokens: 8192, max_tokens: 32000,
stream: true, stream: true,
tools, tools,
thinking: { type: 'enabled', budget_tokens: 16000 }, thinking: { type: 'enabled', budget_tokens: 16000 },
@@ -93,7 +93,7 @@ Please do this step by step, reading each file before modifying.`
const result2 = await streamRequest({ const result2 = await streamRequest({
model: 'claude-opus-4-5-thinking', model: 'claude-opus-4-5-thinking',
max_tokens: 8192, max_tokens: 32000,
stream: true, stream: true,
tools, tools,
thinking: { type: 'enabled', budget_tokens: 16000 }, thinking: { type: 'enabled', budget_tokens: 16000 },

View File

@@ -33,7 +33,7 @@ async function runTests() {
const turn1 = await streamRequest({ const turn1 = await streamRequest({
model: 'claude-sonnet-4-5-thinking', model: 'claude-sonnet-4-5-thinking',
max_tokens: 4096, max_tokens: 16000,
stream: true, stream: true,
tools, tools,
thinking: { type: 'enabled', budget_tokens: 10000 }, thinking: { type: 'enabled', budget_tokens: 10000 },
@@ -102,7 +102,7 @@ drwxr-xr-x 4 user staff 128 Dec 19 10:00 tests`
const turn2 = await streamRequest({ const turn2 = await streamRequest({
model: 'claude-sonnet-4-5-thinking', model: 'claude-sonnet-4-5-thinking',
max_tokens: 4096, max_tokens: 16000,
stream: true, stream: true,
tools, tools,
thinking: { type: 'enabled', budget_tokens: 10000 }, thinking: { type: 'enabled', budget_tokens: 10000 },

View File

@@ -38,7 +38,7 @@ async function runTests() {
const turn1 = await makeRequest({ const turn1 = await makeRequest({
model: 'claude-sonnet-4-5-thinking', model: 'claude-sonnet-4-5-thinking',
max_tokens: 4096, max_tokens: 16000,
stream: false, stream: false,
tools, tools,
thinking: { type: 'enabled', budget_tokens: 10000 }, thinking: { type: 'enabled', budget_tokens: 10000 },
@@ -92,7 +92,7 @@ async function runTests() {
const turn2 = await makeRequest({ const turn2 = await makeRequest({
model: 'claude-sonnet-4-5-thinking', model: 'claude-sonnet-4-5-thinking',
max_tokens: 4096, max_tokens: 16000,
stream: false, stream: false,
tools, tools,
thinking: { type: 'enabled', budget_tokens: 10000 }, thinking: { type: 'enabled', budget_tokens: 10000 },
@@ -156,7 +156,7 @@ async function runTests() {
const turn3 = await makeRequest({ const turn3 = await makeRequest({
model: 'claude-sonnet-4-5-thinking', model: 'claude-sonnet-4-5-thinking',
max_tokens: 4096, max_tokens: 16000,
stream: false, stream: false,
tools, tools,
thinking: { type: 'enabled', budget_tokens: 10000 }, thinking: { type: 'enabled', budget_tokens: 10000 },

View File

@@ -31,7 +31,7 @@ async function runTests() {
const turn1Result = await streamRequest({ const turn1Result = await streamRequest({
model: 'claude-sonnet-4-5-thinking', model: 'claude-sonnet-4-5-thinking',
max_tokens: 4096, max_tokens: 16000,
stream: true, stream: true,
tools, tools,
thinking: { type: 'enabled', budget_tokens: 10000 }, thinking: { type: 'enabled', budget_tokens: 10000 },
@@ -95,7 +95,7 @@ async function runTests() {
const turn2Result = await streamRequest({ const turn2Result = await streamRequest({
model: 'claude-sonnet-4-5-thinking', model: 'claude-sonnet-4-5-thinking',
max_tokens: 4096, max_tokens: 16000,
stream: true, stream: true,
tools, tools,
thinking: { type: 'enabled', budget_tokens: 10000 }, thinking: { type: 'enabled', budget_tokens: 10000 },