支持 Gemini Interactions 与 Antigravity 配额精细化

This commit is contained in:
MMEXA
2026-07-08 22:34:29 +08:00
parent 1ca83ca3fb
commit 80a6579766
41 changed files with 1072 additions and 225 deletions
@@ -0,0 +1,124 @@
import { describe, expect, it, vi } from 'vitest'
import { createApp, defineComponent, h } from 'vue'
import AntigravityQuotaDialog from '@/features/providers/components/AntigravityQuotaDialog.vue'
import type { UpstreamMetadata } from '@/api/endpoints/types'
vi.mock('@/components/ui', async () => {
const { defineComponent, h } = await import('vue')
const passthrough = (name: string) => defineComponent({
name,
setup(_, { slots }) {
return () => h('div', [
slots.headerActions?.(),
slots.default?.(),
slots.footer?.(),
])
},
})
return {
Dialog: passthrough('DialogStub'),
DropdownMenu: passthrough('DropdownMenuStub'),
DropdownMenuTrigger: passthrough('DropdownMenuTriggerStub'),
DropdownMenuContent: passthrough('DropdownMenuContentStub'),
DropdownMenuItem: defineComponent({
name: 'DropdownMenuItemStub',
emits: ['select'],
setup(_, { emit, slots }) {
return () => h('button', { type: 'button', onClick: () => emit('select') }, slots.default?.())
},
}),
}
})
vi.mock('@/components/ui/button.vue', async () => {
const { defineComponent, h } = await import('vue')
return {
default: defineComponent({
name: 'ButtonStub',
setup(_, { attrs, slots }) {
return () => h('button', { ...attrs, type: 'button' }, slots.default?.())
},
}),
}
})
vi.mock('lucide-vue-next', async () => {
const { defineComponent, h } = await import('vue')
const Icon = defineComponent({
name: 'IconStub',
setup() {
return () => h('span')
},
})
return {
BarChart3: Icon,
Loader2: Icon,
Play: Icon,
}
})
vi.mock('@/api/endpoints/providers', () => ({
testModel: vi.fn(),
}))
vi.mock('@/composables/useToast', () => ({
useToast: () => ({
error: vi.fn(),
success: vi.fn(),
}),
}))
vi.mock('@/utils/errorParser', () => ({
parseApiError: (value: unknown) => String(value),
}))
function mount(metadata: UpstreamMetadata) {
const root = document.createElement('div')
document.body.appendChild(root)
const app = createApp(defineComponent({
setup() {
return () => h(AntigravityQuotaDialog, {
open: true,
metadata,
keyName: 'Key-1',
})
},
}))
app.mount(root)
return {
root,
unmount: () => {
app.unmount()
root.remove()
},
}
}
describe('AntigravityQuotaDialog', () => {
it('renders model display names without losing raw model identifiers', () => {
const { root, unmount } = mount({
antigravity: {
quota_by_model: {
'RateLimitResetCredit_05cbb6eeeb9c81918e011d8300f9ebfb': {
display_name: 'RateLimitResetCredit_05cbb6eeeb9c81918e011d8300f9ebfb',
remaining_fraction: 0.25,
used_percent: 75,
},
},
},
})
expect(root.textContent).toContain('Key-1')
expect(root.textContent).toContain('RateLimitResetCredit_05cbb6eeeb9c81918e011d8300f9ebfb')
expect(root.textContent).toContain('25.0%')
unmount()
})
})
@@ -5,6 +5,7 @@ import { getDefaultEndpointBaseUrl, getDefaultEndpointPath } from '../endpoint-d
const apiFormats = [
{ value: 'openai:chat', default_path: '/v1/chat/completions' },
{ value: 'gemini:generate_content', default_path: '/v1beta/models/{model}:{action}' },
{ value: 'gemini:interactions', default_path: '/v1/interactions' },
{ value: 'gemini:embedding', default_path: '/v1beta/models/{model}:embedContent' },
{ value: 'gemini:video', default_path: '/v1beta/models/{model}:predictLongRunning' },
{ value: 'openai:responses', default_path: '/v1/responses' },
@@ -31,6 +32,12 @@ describe('endpoint default paths', () => {
apiFormats,
})).toBe('/models/{model}:embedContent')
expect(getDefaultEndpointPath({
apiFormat: 'gemini:interactions',
providerType: 'custom',
apiFormats,
})).toBe('/interactions')
expect(getDefaultEndpointPath({
apiFormat: 'gemini:video',
providerType: 'custom',
@@ -218,6 +225,11 @@ describe('endpoint default paths', () => {
baseUrl: 'https://generativelanguage.googleapis.com',
})).toBe('https://generativelanguage.googleapis.com/v1beta')
expect(getDefaultEndpointBaseUrl({
apiFormat: 'gemini:interactions',
baseUrl: 'https://generativelanguage.googleapis.com',
})).toBe('https://generativelanguage.googleapis.com/v1')
expect(getDefaultEndpointBaseUrl({
apiFormat: 'gemini:video',
baseUrl: 'https://generativelanguage.googleapis.com',