Skip to content

Commit

Permalink
test: 修复一些测试
Browse files Browse the repository at this point in the history
  • Loading branch information
rxliuli committed Aug 24, 2024
1 parent 027c231 commit 9dbed19
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 56 deletions.
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Introduction

Provides the same agent OpenAI API interface for different LLM models and supports deployment to any Edge Runtime environment.
Provides the same proxy OpenAI API interface for different LLM models, and supports deployment to any Edge Runtime environment.

Supported models

Expand All @@ -18,25 +18,25 @@ Supported models

Environment variables

- `API_KEY`: proxy API Key, which must be set when calling the proxy API
- `API_KEY`: Proxy API Key, required when calling the proxy API

- OpenAI: Support OpenAI models, such as `gpt-4o-mini`
- OpenAI: Supports OpenAI models, e.g. `gpt-4o-mini`
- `OPENAI_API_KEY`: OpenAI API Key
- VertexAI Anthropic: Support Anthropic models on Google Vertex AI, such as `claude-3-5-sonnet@20240620`
- VertexAI Anthropic: Supports Anthropic models on Google Vertex AI, e.g. `claude-3-5-sonnet@20240620`
- `VERTEX_ANTROPIC_GOOGLE_SA_CLIENT_EMAIL`: Google Cloud Service Account Email
- `VERTEX_ANTROPIC_GOOGLE_SA_PRIVATE_KEY`: Google Cloud Service Account Private Key
- `VERTEX_ANTROPIC_REGION`: Google Vertex AI Anthropic Region
- `VERTEX_ANTROPIC_PROJECTID`: Google Vertex AI Anthropic Project ID
- Anthropic: Support Anthropic models, such as `claude-3-5-sonnet-20240620`
- Anthropic: Supports Anthropic models, e.g. `claude-3-5-sonnet-20240620`
- `ANTROPIC_API_KEY`: Anthropic API Key
- Google Gemini: Support Google Gemini models, such as `gemini-1.5-flash`
- Google Gemini: Supports Google Gemini models, e.g. `gemini-1.5-flash`
- `GOOGLE_GEN_AI_API_KEY`: Google Gemini API Key

## Usage

Once deployed successfully, different models can be called through OpenAIs API interface.
Once deployed successfully, you can call different models through OpenAI's API interface.

For example, calling OpenAIs API interface:
For example, calling OpenAI's API interface:

```bash
curl http://localhost:8787/v1/chat/completions \
Expand All @@ -53,7 +53,7 @@ curl http://localhost:8787/v1/chat/completions \
}'
```

Or call Anthropics API interface:
Or calling Anthropic's API interface:

```bash
curl http://localhost:8787/v1/chat/completions \
Expand All @@ -70,7 +70,7 @@ curl http://localhost:8787/v1/chat/completions \
}'
```

And can be used in OpenAIs official SDK, for example:
And it can be used in OpenAI's official SDK, for example:

```ts
const openai = new OpenAI({
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"type": "module",
"scripts": {
"dev": "wrangler dev src/index.ts",
"deploy": "wrangler deploy --minify src/index.ts"
"deploy": "wrangler deploy --minify src/index.ts",
"test": "vitest run"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.27.0",
Expand Down
30 changes: 17 additions & 13 deletions src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { beforeAll, beforeEach, describe, expect, it } from 'vitest'
import app from '..'
import OpenAI from 'openai'
import { omit } from 'lodash-es'
import { omit, pick } from 'lodash-es'
import { AnthropicVertex } from '@anthropic-ai/vertex-sdk'
import { GoogleAuth } from 'google-auth-library'
import { TextBlock } from '@anthropic-ai/sdk/resources/messages.mjs'
import { anthropic } from '../llm/anthropic'
import { testClient } from 'hono/testing'

const MOCK_ENV = {
API_KEY: import.meta.env.VITE_API_KEY,
Expand Down Expand Up @@ -196,26 +197,29 @@ describe('stream', () => {
describe('list models', async () => {
it('no api key', async () => {
const r = (await (
await app.request('/v1/models', {}, {})
await app.request(
'/v1/models',
{
headers: { Authorization: 'Bearer ' + import.meta.env.VITE_API_KEY },
},
pick(MOCK_ENV, 'API_KEY'),
)
).json()) as OpenAI.Models.ModelsPage
expect(r.data).empty
})
it('anthropic api key', async () => {
const r = (await (
await app.request(
'/v1/models',
{},
{ ANTROPIC_API_KEY: import.meta.env.VITE_ANTROPIC_API_KEY },
{
headers: {
Authorization: 'Bearer ' + import.meta.env.VITE_API_KEY,
},
},
pick(MOCK_ENV, 'API_KEY', 'ANTROPIC_API_KEY'),
)
).json()) as OpenAI.Models.ModelsPage
expect(r.data).deep.eq(
anthropic({} as any).supportModels.map((it) => ({
id: it,
object: 'model',
owned_by: 'system',
created: 0,
})),
)
expect(r.data.map((it) => it.id)).deep.eq(anthropic(MOCK_ENV).supportModels)
})
it('openai api key', async () => {
const client = new OpenAI({
Expand All @@ -225,6 +229,6 @@ describe('list models', async () => {
},
})
const r = await client.models.list()
console.log(r)
expect(r.data).not.empty
})
})
12 changes: 6 additions & 6 deletions src/claude/__tests__/authenticate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import { it, expect } from 'vitest'

it('should authenticate', async () => {
const token = await authenticate({
clientEmail: process.env.VITE_GOOGLE_SA_CLIENT_EMAIL!,
privateKey: process.env.VITE_GOOGLE_SA_PRIVATE_KEY!,
clientEmail: import.meta.env.VITE_VERTEX_ANTROPIC_GOOGLE_SA_CLIENT_EMAIL!,
privateKey: import.meta.env.VITE_VERTEX_ANTROPIC_GOOGLE_SA_PRIVATE_KEY!,
})
expect(token).toBeDefined()
})

it('should call anthropic vertex', async () => {
const token = await authenticate({
clientEmail: process.env.VITE_GOOGLE_SA_CLIENT_EMAIL!,
privateKey: process.env.VITE_GOOGLE_SA_PRIVATE_KEY!,
clientEmail: import.meta.env.VITE_VERTEX_ANTROPIC_GOOGLE_SA_CLIENT_EMAIL!,
privateKey: import.meta.env.VITE_VERTEX_ANTROPIC_GOOGLE_SA_PRIVATE_KEY!,
})
const client = new AnthropicVertexWeb({
accessToken: token.access_token,
Expand Down Expand Up @@ -42,8 +42,8 @@ function buildUrl({

it('should authenticate on edge runtime', async () => {
const token = await authenticate({
clientEmail: process.env.VITE_GOOGLE_SA_CLIENT_EMAIL!,
privateKey: process.env.VITE_GOOGLE_SA_PRIVATE_KEY!,
clientEmail: import.meta.env.VITE_VERTEX_ANTROPIC_GOOGLE_SA_CLIENT_EMAIL!,
privateKey: import.meta.env.VITE_VERTEX_ANTROPIC_GOOGLE_SA_PRIVATE_KEY!,
})
expect(token).toBeDefined()
const url = buildUrl({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { expect, it } from 'vitest'
import { AnthropicVertexWeb } from '../web'
import { AnthropicVertex, AnthropicVertex as OriginAnthropicVertex } from '@anthropic-ai/vertex-sdk'
import {
AnthropicVertex,
AnthropicVertex as OriginAnthropicVertex,
} from '@anthropic-ai/vertex-sdk'
import { authenticate } from '../authenticate'
import { omit } from 'lodash-es'
import { ChatAnthropic } from 'langchain-anthropic-edge'
Expand Down Expand Up @@ -53,7 +56,7 @@ it('should call Custom Client and Origin Client', async () => {
expect(r1).not.undefined
expect(r2).not.undefined
expect(omit(r1, 'id')).deep.equal(omit(r2, 'id'))
}, 10_000)
}, 20_000)

it('Call Anthropic Vertex on nodejs', async () => {
const client = new AnthropicVertex({
Expand Down
3 changes: 3 additions & 0 deletions src/claude/authenticate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ export async function authenticate(options: {
clientEmail: string
privateKey: string
}): Promise<Token> {
if (!options.clientEmail || !options.privateKey) {
throw new Error('clientEmail and privateKey are required')
}
if (token === null) {
token = await createToken(options)
} else if (token.expires_at < Math.floor(Date.now() / 1000)) {
Expand Down
44 changes: 22 additions & 22 deletions src/llm/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ export function anthropicBase(
signal.onabort = () => stream.controller.abort()
const chunks: AnthropicTypes.Messages.RawMessageStreamEvent[] = []
let start: AnthropicTypes.Messages.Message | undefined
let delta: AnthropicTypes.Messages.MessageDeltaEvent | undefined
for await (const it of stream) {
chunks.push(it)
const fileds = () => ({
Expand All @@ -176,28 +175,29 @@ export function anthropicBase(
],
} as OpenAI.ChatCompletionChunk
} else if (it.type === 'message_delta') {
delta = it
yield {
...fileds(),
choices: [
{
index: 0,
delta: {},
finish_reason: 'stop',
if (req.stream_options?.include_usage) {
yield {
...fileds(),
choices: [],
usage: {
prompt_tokens: start!.usage.input_tokens,
completion_tokens: it!.usage.output_tokens,
total_tokens:
start!.usage.input_tokens + it!.usage.output_tokens,
},
],
} as OpenAI.ChatCompletionChunk
} else if (it.type === 'message_stop') {
yield {
...fileds(),
choices: [],
usage: {
prompt_tokens: start!.usage.input_tokens,
completion_tokens: delta!.usage.output_tokens,
total_tokens:
start!.usage.input_tokens + delta!.usage.output_tokens,
},
} as OpenAI.ChatCompletionChunk
} as OpenAI.ChatCompletionChunk
} else {
yield {
...fileds(),
choices: [
{
index: 0,
delta: {},
finish_reason: 'stop',
},
],
} as OpenAI.ChatCompletionChunk
}
}
}
},
Expand Down
19 changes: 17 additions & 2 deletions src/llm/google.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IChat } from './base'
import { GoogleGenerativeAI, Tool } from '@google/generative-ai'
import { GoogleGenerativeAI } from '@google/generative-ai'
import GoogleAI from '@google/generative-ai'
import OpenAI from 'openai'

Expand Down Expand Up @@ -128,7 +128,11 @@ export function google(env: Record<string, string>): IChat {
],
} as OpenAI.ChatCompletionChunk
}
if (req.stream_options?.include_usage && last) {
if (!last) {
throw new Error('No response from google')
}

if (req.stream_options?.include_usage) {
yield {
...fields(),
choices: [],
Expand All @@ -138,6 +142,17 @@ export function google(env: Record<string, string>): IChat {
total_tokens: last.usageMetadata!.totalTokenCount,
},
} as OpenAI.ChatCompletionChunk
} else {
yield {
...fields(),
choices: [
{
index: 0,
delta: {},
finish_reason: 'stop',
},
],
} as OpenAI.ChatCompletionChunk
}
},
}
Expand Down

0 comments on commit 9dbed19

Please sign in to comment.