Skip to content

Commit

Permalink
Update external client check + google (#193)
Browse files Browse the repository at this point in the history
  • Loading branch information
roodboi authored Jan 14, 2025
1 parent bf240b2 commit da449de
Show file tree
Hide file tree
Showing 8 changed files with 307 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/eleven-needles-fry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@instructor-ai/instructor": minor
---

update peer deps + remove baseUrl check on generic client type guard
2 changes: 1 addition & 1 deletion .github/workflows/test-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
ANYSCALE_API_KEY: ${{ secrets.ANYSCALE_API_KEY }}
TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
steps:
- uses: actions/checkout@v3
with:
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ jobs:
ANYSCALE_API_KEY: ${{ secrets.ANYSCALE_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }}

GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}

steps:
- uses: actions/checkout@v3
with:
Expand Down
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
nodejs 20.9.0
bun 1.0.15
bun 1.1.43
python 3.12.1
Binary file modified bun.lockb
Binary file not shown.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"zod": ">=3.23.8"
},
"devDependencies": {
"@anthropic-ai/sdk": "0.29.2",
"@anthropic-ai/sdk": "0.33.1",
"@changesets/changelog-github": "^0.5.0",
"@changesets/cli": "^2.27.1",
"@ianvs/prettier-plugin-sort-imports": "4.1.0",
Expand All @@ -75,8 +75,8 @@
"eslint-plugin-only-warn": "^1.1.0",
"eslint-plugin-prettier": "^5.1.2",
"husky": "^8.0.3",
"llm-polyglot": "2.2.0",
"openai": "4.68.1",
"llm-polyglot": "2.5.0",
"openai": "4.78.1",
"prettier": "latest",
"ts-inference-check": "^0.3.0",
"tsup": "^8.0.1",
Expand Down
1 change: 0 additions & 1 deletion src/instructor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,6 @@ function isGenericClient(client: any): client is GenericClient {
return (
typeof client === "object" &&
client !== null &&
"baseURL" in client &&
"chat" in client &&
typeof client.chat === "object" &&
"completions" in client.chat &&
Expand Down
295 changes: 295 additions & 0 deletions tests/google.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
import Instructor from "@/index"
import { omit } from "@/lib"
import { describe, expect, test } from "bun:test"
import { createLLMClient } from "llm-polyglot"
import z from "zod"

const googleClient = createLLMClient({
provider: "google",
apiKey: process.env.GEMINI_API_KEY
})

describe("LLMClient google Provider - mode: TOOLS", () => {
const instructor = Instructor({
client: googleClient,
mode: "TOOLS"
})

test("basic completion", async () => {
const completion = await instructor.chat.completions.create({
model: "gemini-1.5-flash-latest",
max_tokens: 1000,
messages: [
{
role: "user",
content: "My name is Dimitri Kennedy."
}
],
response_model: {
name: "extract_name",
schema: z.object({
name: z.string()
})
}
})

expect(omit(["_meta"], completion)).toEqual({ name: "Dimitri Kennedy" })
})

test("complex schema", async () => {
const completion = await instructor.chat.completions.create({
model: "gemini-1.5-flash-latest",
max_tokens: 1000,
messages: [
{
role: "user",
content: `User Data Submission:
First Name: John
Last Name: Doe
Contact Details:
Email: [email protected]
Phone Number: 555-1234
Job History:
Company Name: Acme Corp
Role: Software Engineer
Years: 5
Company Name: Globex Inc.
Role: Lead Developer
Years: 3
Skills:
Programming
Leadership
Communication
`
}
],
response_model: {
name: "process_user_data",
schema: z.object({
story: z
.string()
.describe("A long and mostly made up story about the user - minimum 500 words"),
userDetails: z.object({
firstName: z.string(),
lastName: z.string(),
contactDetails: z.object({
email: z.string(),
phoneNumber: z.string().optional()
})
}),
jobHistory: z.array(
z.object({
companyName: z.string(),
role: z.string(),
years: z.number().optional()
})
),
skills: z.array(z.string())
})
}
})

expect(omit(["_meta", "story"], completion)).toEqual({
userDetails: {
firstName: "John",
lastName: "Doe",
contactDetails: {
email: "[email protected]",
phoneNumber: "555-1234"
}
},
jobHistory: [
{
companyName: "Acme Corp",
role: "Software Engineer",
years: 5
},
{
companyName: "Globex Inc.",
role: "Lead Developer",
years: 3
}
],
skills: ["Programming", "Leadership", "Communication"]
})
})
})

describe("LLMClient google Provider - mode: TOOLS - stream", () => {
const instructor = Instructor({
client: googleClient,
mode: "TOOLS"
})

test("basic completion", async () => {
const completion = await instructor.chat.completions.create({
model: "gemini-1.5-flash-latest",
stream: true,
max_tokens: 1000,
messages: [
{
role: "user",
content: "My name is Dimitri Kennedy."
}
],
response_model: {
name: "extract_name",
schema: z.object({
name: z.string()
})
}
})

let final = {}

for await (const result of completion) {
final = result
}

//@ts-expect-error ignore for testing
expect(omit(["_meta"], final)).toEqual({ name: "Dimitri Kennedy" })
})

test("complex schema", async () => {
const completion = await instructor.chat.completions.create({
model: "gemini-1.5-flash-latest",
max_tokens: 1000,
stream: true,
messages: [
{
role: "user",
content: `User Data Submission:
First Name: John
Last Name: Doe
Contact Details:
Email: [email protected]
Phone Number: 555-1234
Job History:
Company Name: Acme Corp
Role: Software Engineer
Years: 5
Company Name: Globex Inc.
Role: Lead Developer
Years: 3
Skills:
Programming
Leadership
Communication
`
}
],
response_model: {
name: "process_user_data",
schema: z.object({
story: z
.string()
.describe("A long and mostly made up story about the user - minimum 500 words"),
userDetails: z.object({
firstName: z.string(),
lastName: z.string(),
contactDetails: z.object({
email: z.string(),
phoneNumber: z.string().optional()
})
}),
jobHistory: z.array(
z.object({
companyName: z.string(),
role: z.string(),
years: z.number().optional()
})
),
skills: z.array(z.string())
})
}
})

let final = {}

for await (const result of completion) {
final = result
}

//@ts-expect-error ignore for testing
expect(omit(["_meta", "story"], final)).toEqual({
userDetails: {
firstName: "John",
lastName: "Doe",
contactDetails: {
email: "[email protected]",
phoneNumber: "555-1234"
}
},
jobHistory: [
{
companyName: "Acme Corp",
role: "Software Engineer",
years: 5
},
{
companyName: "Globex Inc.",
role: "Lead Developer",
years: 3
}
],
skills: ["Programming", "Leadership", "Communication"]
})
})
})

describe("LLMClient google Provider - Grounding", () => {
test("grounding with structured output", async () => {
const completion = await googleClient.chat.completions.create({
model: "gemini-1.5-flash-latest",
messages: [
{
role: "user",
content:
"You are a food critic. Review the most popular Italian restaurants in Boston's North End, focusing on their traditional dishes."
}
],
groundingThreshold: 0.7,
max_tokens: 1000,
stream: false
})

expect(completion.choices?.[0]?.message?.content).toBeTruthy()
})

test("grounding with different thresholds", async () => {
const highThreshold = await googleClient.chat.completions.create({
model: "gemini-1.5-flash-latest",
messages: [
{
role: "user",
content: "What are the current top-rated Italian restaurants in Boston's North End?"
}
],
groundingThreshold: 0.9,
max_tokens: 1000
})

// Lower threshold for more general responses
const lowThreshold = await googleClient.chat.completions.create({
model: "gemini-1.5-flash-latest",
messages: [
{
role: "user",
content: "What are the current top-rated Italian restaurants in Boston's North End?"
}
],
groundingThreshold: 0.5,
max_tokens: 1000
})

expect(highThreshold.choices?.[0]?.message?.content).not.toBe(
lowThreshold.choices?.[0]?.message?.content
)
})
})

0 comments on commit da449de

Please sign in to comment.