@livekit/agents-plugin-cerebras 1.4.9 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/llm.test.cjs +4 -3
- package/dist/llm.test.cjs.map +1 -1
- package/dist/llm.test.js +4 -3
- package/dist/llm.test.js.map +1 -1
- package/package.json +8 -8
- package/src/llm.test.ts +4 -3
package/dist/index.cjs
CHANGED
package/dist/index.js
CHANGED
package/dist/llm.test.cjs
CHANGED
|
@@ -76,8 +76,9 @@ class WeatherAgent extends import_agents.voice.Agent {
|
|
|
76
76
|
constructor() {
|
|
77
77
|
super({
|
|
78
78
|
instructions: "You are a helpful assistant.",
|
|
79
|
-
tools:
|
|
80
|
-
|
|
79
|
+
tools: [
|
|
80
|
+
import_agents.llm.tool({
|
|
81
|
+
name: "get_weather",
|
|
81
82
|
description: "Get the current weather for a location.",
|
|
82
83
|
parameters: import_zod.z.object({
|
|
83
84
|
location: import_zod.z.string().describe("The city name")
|
|
@@ -86,7 +87,7 @@ class WeatherAgent extends import_agents.voice.Agent {
|
|
|
86
87
|
return `The weather in ${location} is sunny, 72\xB0F.`;
|
|
87
88
|
}
|
|
88
89
|
})
|
|
89
|
-
|
|
90
|
+
]
|
|
90
91
|
});
|
|
91
92
|
}
|
|
92
93
|
}
|
package/dist/llm.test.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/llm.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { llm, voice } from '@livekit/agents';\nimport { encode } from '@msgpack/msgpack';\nimport { gzipSync } from 'node:zlib';\nimport OpenAI from 'openai';\nimport { assert, describe, expect, it } from 'vitest';\nimport { z } from 'zod';\nimport type { LLMOptions } from './llm.js';\nimport { LLM } from './llm.js';\n\nassert(process.env.CEREBRAS_API_KEY, 'CEREBRAS_API_KEY must be set');\n\n// llama3.1-8b is fast and has generous rate limits but can't do tool calls reliably;\n// qwen-3-235b is needed for function calling but has tight per-minute token quotas.\nconst CHAT_MODEL = 'llama3.1-8b';\nconst TOOL_MODEL = 'qwen-3-235b-a22b-instruct-2507';\n\ninterface CapturedRequest {\n url: string;\n headers: Headers;\n}\n\n/**\n * Wraps a real fetch, applying msgpack/gzip compression and capturing outgoing\n * request metadata for assertion. TypeScript equivalent of Python's\n * `HeaderCapturingTransport` + `_CerebrasClient`.\n */\nfunction createCapturingFetch(opts: { useMsgpack: boolean; useGzip: boolean }): {\n fetch: typeof globalThis.fetch;\n capturedRequests: CapturedRequest[];\n} {\n const capturedRequests: CapturedRequest[] = [];\n\n const fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n if (init?.method === 'POST' && init.body && typeof init.body === 'string') {\n const headers = new Headers(init.headers);\n\n let body: Uint8Array;\n if (opts.useMsgpack) {\n body = encode(JSON.parse(init.body));\n headers.set('Content-Type', 'application/vnd.msgpack');\n } else {\n body = new TextEncoder().encode(init.body);\n }\n\n if (opts.useGzip) {\n body = gzipSync(body, { level: 5 });\n headers.set('Content-Encoding', 'gzip');\n }\n\n capturedRequests.push({ url: extractUrl(input), headers });\n return globalThis.fetch(input, { ...init, body: Buffer.from(body), headers });\n }\n\n capturedRequests.push({ url: extractUrl(input), headers: new Headers(init?.headers) });\n return globalThis.fetch(input, init);\n };\n\n return { fetch: fetch as typeof globalThis.fetch, capturedRequests };\n}\n\nfunction extractUrl(input: RequestInfo | URL): string {\n if (typeof input === 'string') return input;\n if (input instanceof URL) return input.toString();\n return input.url;\n}\n\nfunction cerebrasLLM(opts: Partial<LLMOptions> = {}): LLM {\n return new LLM({ model: CHAT_MODEL, ...opts });\n}\n\nfunction cerebrasLLMWithCapture(opts: { useGzip: boolean; useMsgpack: boolean }): {\n llm: LLM;\n capturedRequests: CapturedRequest[];\n} {\n const { fetch, capturedRequests } = createCapturingFetch(opts);\n const client = new OpenAI({\n apiKey: process.env.CEREBRAS_API_KEY,\n baseURL: 'https://api.cerebras.ai/v1',\n fetch,\n });\n return { llm: new LLM({ model: CHAT_MODEL, client }), capturedRequests };\n}\n\nclass WeatherAgent extends voice.Agent {\n constructor() {\n super({\n instructions: 'You are a helpful assistant.',\n tools: {\n get_weather: llm.tool({\n description: 'Get the current weather for a location.',\n parameters: z.object({\n location: z.string().describe('The city name'),\n }),\n execute: async ({ location }) => {\n return `The weather in ${location} is sunny, 72°F.`;\n },\n }),\n },\n });\n }\n}\n\ndescribe('Cerebras', { timeout: 30_000 }, () => {\n it('basic chat completion returns a non-empty assistant message', async () => {\n const session = new voice.AgentSession({ llm: cerebrasLLM() });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n });\n\n it('LLM can invoke a tool and the result is returned', async () => {\n const session = new voice.AgentSession({ llm: new LLM({ model: TOOL_MODEL }) });\n await session.start({ agent: new WeatherAgent() });\n\n const result = session.run({ userInput: 'What is the weather in Tokyo?' });\n await result.wait();\n\n result.expect.nextEvent().isFunctionCall({\n name: 'get_weather',\n args: { location: 'Tokyo' },\n });\n result.expect.nextEvent().isFunctionCallOutput({\n output: JSON.stringify('The weather in Tokyo is sunny, 72°F.'),\n });\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n });\n\n it('gzip-only sends Content-Encoding: gzip with JSON content type', async () => {\n const { llm: model, capturedRequests } = cerebrasLLMWithCapture({\n useGzip: true,\n useMsgpack: false,\n });\n const session = new voice.AgentSession({ llm: model });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n\n const chatReqs = capturedRequests.filter((r) => r.url.includes('/chat/completions'));\n expect(chatReqs.length).toBeGreaterThan(0);\n expect(chatReqs[0]!.headers.get('content-type')).toBe('application/json');\n expect(chatReqs[0]!.headers.get('content-encoding')).toBe('gzip');\n });\n\n it('msgpack-only sends Content-Type: application/vnd.msgpack without gzip', async () => {\n const { llm: model, capturedRequests } = cerebrasLLMWithCapture({\n useGzip: false,\n useMsgpack: true,\n });\n const session = new voice.AgentSession({ llm: model });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n\n const chatReqs = capturedRequests.filter((r) => r.url.includes('/chat/completions'));\n expect(chatReqs.length).toBeGreaterThan(0);\n expect(chatReqs[0]!.headers.get('content-type')).toBe('application/vnd.msgpack');\n expect(chatReqs[0]!.headers.get('content-encoding')).toBeNull();\n });\n\n it('both flags send msgpack content type with gzip encoding', async () => {\n const { llm: model, capturedRequests } = cerebrasLLMWithCapture({\n useGzip: true,\n useMsgpack: true,\n });\n const session = new voice.AgentSession({ llm: model });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n\n const chatReqs = capturedRequests.filter((r) => r.url.includes('/chat/completions'));\n expect(chatReqs.length).toBeGreaterThan(0);\n expect(chatReqs[0]!.headers.get('content-type')).toBe('application/vnd.msgpack');\n expect(chatReqs[0]!.headers.get('content-encoding')).toBe('gzip');\n });\n\n it('with both flags off sends standard JSON without gzip', async () => {\n const session = new voice.AgentSession({\n llm: cerebrasLLM({ gzipCompression: false, msgpackEncoding: false }),\n });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n });\n\n it('streaming chat returns content via the LLM directly', async () => {\n const model = cerebrasLLM();\n const chatCtx = new llm.ChatContext();\n chatCtx.addMessage({ role: 'system', content: 'You are a helpful assistant.' });\n chatCtx.addMessage({ role: 'user', content: 'Count from 1 to 5.' });\n\n const stream = model.chat({ chatCtx });\n let text = '';\n for await (const chunk of stream) {\n if (chunk.delta?.content) {\n text += chunk.delta.content;\n }\n }\n\n expect(text.length).toBeGreaterThan(0);\n expect(text).toContain('3');\n });\n});\n"],"mappings":"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","names":["OpenAI"]}
|
|
1
|
+
{"version":3,"sources":["../src/llm.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { llm, voice } from '@livekit/agents';\nimport { encode } from '@msgpack/msgpack';\nimport { gzipSync } from 'node:zlib';\nimport OpenAI from 'openai';\nimport { assert, describe, expect, it } from 'vitest';\nimport { z } from 'zod';\nimport type { LLMOptions } from './llm.js';\nimport { LLM } from './llm.js';\n\nassert(process.env.CEREBRAS_API_KEY, 'CEREBRAS_API_KEY must be set');\n\n// llama3.1-8b is fast and has generous rate limits but can't do tool calls reliably;\n// qwen-3-235b is needed for function calling but has tight per-minute token quotas.\nconst CHAT_MODEL = 'llama3.1-8b';\nconst TOOL_MODEL = 'qwen-3-235b-a22b-instruct-2507';\n\ninterface CapturedRequest {\n url: string;\n headers: Headers;\n}\n\n/**\n * Wraps a real fetch, applying msgpack/gzip compression and capturing outgoing\n * request metadata for assertion. TypeScript equivalent of Python's\n * `HeaderCapturingTransport` + `_CerebrasClient`.\n */\nfunction createCapturingFetch(opts: { useMsgpack: boolean; useGzip: boolean }): {\n fetch: typeof globalThis.fetch;\n capturedRequests: CapturedRequest[];\n} {\n const capturedRequests: CapturedRequest[] = [];\n\n const fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n if (init?.method === 'POST' && init.body && typeof init.body === 'string') {\n const headers = new Headers(init.headers);\n\n let body: Uint8Array;\n if (opts.useMsgpack) {\n body = encode(JSON.parse(init.body));\n headers.set('Content-Type', 'application/vnd.msgpack');\n } else {\n body = new TextEncoder().encode(init.body);\n }\n\n if (opts.useGzip) {\n body = gzipSync(body, { level: 5 });\n headers.set('Content-Encoding', 'gzip');\n }\n\n capturedRequests.push({ url: extractUrl(input), headers });\n return globalThis.fetch(input, { ...init, body: Buffer.from(body), headers });\n }\n\n capturedRequests.push({ url: extractUrl(input), headers: new Headers(init?.headers) });\n return globalThis.fetch(input, init);\n };\n\n return { fetch: fetch as typeof globalThis.fetch, capturedRequests };\n}\n\nfunction extractUrl(input: RequestInfo | URL): string {\n if (typeof input === 'string') return input;\n if (input instanceof URL) return input.toString();\n return input.url;\n}\n\nfunction cerebrasLLM(opts: Partial<LLMOptions> = {}): LLM {\n return new LLM({ model: CHAT_MODEL, ...opts });\n}\n\nfunction cerebrasLLMWithCapture(opts: { useGzip: boolean; useMsgpack: boolean }): {\n llm: LLM;\n capturedRequests: CapturedRequest[];\n} {\n const { fetch, capturedRequests } = createCapturingFetch(opts);\n const client = new OpenAI({\n apiKey: process.env.CEREBRAS_API_KEY,\n baseURL: 'https://api.cerebras.ai/v1',\n fetch,\n });\n return { llm: new LLM({ model: CHAT_MODEL, client }), capturedRequests };\n}\n\nclass WeatherAgent extends voice.Agent {\n constructor() {\n super({\n instructions: 'You are a helpful assistant.',\n tools: [\n llm.tool({\n name: 'get_weather',\n description: 'Get the current weather for a location.',\n parameters: z.object({\n location: z.string().describe('The city name'),\n }),\n execute: async ({ location }) => {\n return `The weather in ${location} is sunny, 72°F.`;\n },\n }),\n ],\n });\n }\n}\n\ndescribe('Cerebras', { timeout: 30_000 }, () => {\n it('basic chat completion returns a non-empty assistant message', async () => {\n const session = new voice.AgentSession({ llm: cerebrasLLM() });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n });\n\n it('LLM can invoke a tool and the result is returned', async () => {\n const session = new voice.AgentSession({ llm: new LLM({ model: TOOL_MODEL }) });\n await session.start({ agent: new WeatherAgent() });\n\n const result = session.run({ userInput: 'What is the weather in Tokyo?' });\n await result.wait();\n\n result.expect.nextEvent().isFunctionCall({\n name: 'get_weather',\n args: { location: 'Tokyo' },\n });\n result.expect.nextEvent().isFunctionCallOutput({\n output: JSON.stringify('The weather in Tokyo is sunny, 72°F.'),\n });\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n });\n\n it('gzip-only sends Content-Encoding: gzip with JSON content type', async () => {\n const { llm: model, capturedRequests } = cerebrasLLMWithCapture({\n useGzip: true,\n useMsgpack: false,\n });\n const session = new voice.AgentSession({ llm: model });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n\n const chatReqs = capturedRequests.filter((r) => r.url.includes('/chat/completions'));\n expect(chatReqs.length).toBeGreaterThan(0);\n expect(chatReqs[0]!.headers.get('content-type')).toBe('application/json');\n expect(chatReqs[0]!.headers.get('content-encoding')).toBe('gzip');\n });\n\n it('msgpack-only sends Content-Type: application/vnd.msgpack without gzip', async () => {\n const { llm: model, capturedRequests } = cerebrasLLMWithCapture({\n useGzip: false,\n useMsgpack: true,\n });\n const session = new voice.AgentSession({ llm: model });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n\n const chatReqs = capturedRequests.filter((r) => r.url.includes('/chat/completions'));\n expect(chatReqs.length).toBeGreaterThan(0);\n expect(chatReqs[0]!.headers.get('content-type')).toBe('application/vnd.msgpack');\n expect(chatReqs[0]!.headers.get('content-encoding')).toBeNull();\n });\n\n it('both flags send msgpack content type with gzip encoding', async () => {\n const { llm: model, capturedRequests } = cerebrasLLMWithCapture({\n useGzip: true,\n useMsgpack: true,\n });\n const session = new voice.AgentSession({ llm: model });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n\n const chatReqs = capturedRequests.filter((r) => r.url.includes('/chat/completions'));\n expect(chatReqs.length).toBeGreaterThan(0);\n expect(chatReqs[0]!.headers.get('content-type')).toBe('application/vnd.msgpack');\n expect(chatReqs[0]!.headers.get('content-encoding')).toBe('gzip');\n });\n\n it('with both flags off sends standard JSON without gzip', async () => {\n const session = new voice.AgentSession({\n llm: cerebrasLLM({ gzipCompression: false, msgpackEncoding: false }),\n });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n });\n\n it('streaming chat returns content via the LLM directly', async () => {\n const model = cerebrasLLM();\n const chatCtx = new llm.ChatContext();\n chatCtx.addMessage({ role: 'system', content: 'You are a helpful assistant.' });\n chatCtx.addMessage({ role: 'user', content: 'Count from 1 to 5.' });\n\n const stream = model.chat({ chatCtx });\n let text = '';\n for await (const chunk of stream) {\n if (chunk.delta?.content) {\n text += chunk.delta.content;\n }\n }\n\n expect(text.length).toBeGreaterThan(0);\n expect(text).toContain('3');\n });\n});\n"],"mappings":"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","names":["OpenAI"]}
|
package/dist/llm.test.js
CHANGED
|
@@ -53,8 +53,9 @@ class WeatherAgent extends voice.Agent {
|
|
|
53
53
|
constructor() {
|
|
54
54
|
super({
|
|
55
55
|
instructions: "You are a helpful assistant.",
|
|
56
|
-
tools:
|
|
57
|
-
|
|
56
|
+
tools: [
|
|
57
|
+
llm.tool({
|
|
58
|
+
name: "get_weather",
|
|
58
59
|
description: "Get the current weather for a location.",
|
|
59
60
|
parameters: z.object({
|
|
60
61
|
location: z.string().describe("The city name")
|
|
@@ -63,7 +64,7 @@ class WeatherAgent extends voice.Agent {
|
|
|
63
64
|
return `The weather in ${location} is sunny, 72\xB0F.`;
|
|
64
65
|
}
|
|
65
66
|
})
|
|
66
|
-
|
|
67
|
+
]
|
|
67
68
|
});
|
|
68
69
|
}
|
|
69
70
|
}
|
package/dist/llm.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/llm.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { llm, voice } from '@livekit/agents';\nimport { encode } from '@msgpack/msgpack';\nimport { gzipSync } from 'node:zlib';\nimport OpenAI from 'openai';\nimport { assert, describe, expect, it } from 'vitest';\nimport { z } from 'zod';\nimport type { LLMOptions } from './llm.js';\nimport { LLM } from './llm.js';\n\nassert(process.env.CEREBRAS_API_KEY, 'CEREBRAS_API_KEY must be set');\n\n// llama3.1-8b is fast and has generous rate limits but can't do tool calls reliably;\n// qwen-3-235b is needed for function calling but has tight per-minute token quotas.\nconst CHAT_MODEL = 'llama3.1-8b';\nconst TOOL_MODEL = 'qwen-3-235b-a22b-instruct-2507';\n\ninterface CapturedRequest {\n url: string;\n headers: Headers;\n}\n\n/**\n * Wraps a real fetch, applying msgpack/gzip compression and capturing outgoing\n * request metadata for assertion. TypeScript equivalent of Python's\n * `HeaderCapturingTransport` + `_CerebrasClient`.\n */\nfunction createCapturingFetch(opts: { useMsgpack: boolean; useGzip: boolean }): {\n fetch: typeof globalThis.fetch;\n capturedRequests: CapturedRequest[];\n} {\n const capturedRequests: CapturedRequest[] = [];\n\n const fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n if (init?.method === 'POST' && init.body && typeof init.body === 'string') {\n const headers = new Headers(init.headers);\n\n let body: Uint8Array;\n if (opts.useMsgpack) {\n body = encode(JSON.parse(init.body));\n headers.set('Content-Type', 'application/vnd.msgpack');\n } else {\n body = new TextEncoder().encode(init.body);\n }\n\n if (opts.useGzip) {\n body = gzipSync(body, { level: 5 });\n headers.set('Content-Encoding', 'gzip');\n }\n\n capturedRequests.push({ url: extractUrl(input), headers });\n return globalThis.fetch(input, { ...init, body: Buffer.from(body), headers });\n }\n\n capturedRequests.push({ url: extractUrl(input), headers: new Headers(init?.headers) });\n return globalThis.fetch(input, init);\n };\n\n return { fetch: fetch as typeof globalThis.fetch, capturedRequests };\n}\n\nfunction extractUrl(input: RequestInfo | URL): string {\n if (typeof input === 'string') return input;\n if (input instanceof URL) return input.toString();\n return input.url;\n}\n\nfunction cerebrasLLM(opts: Partial<LLMOptions> = {}): LLM {\n return new LLM({ model: CHAT_MODEL, ...opts });\n}\n\nfunction cerebrasLLMWithCapture(opts: { useGzip: boolean; useMsgpack: boolean }): {\n llm: LLM;\n capturedRequests: CapturedRequest[];\n} {\n const { fetch, capturedRequests } = createCapturingFetch(opts);\n const client = new OpenAI({\n apiKey: process.env.CEREBRAS_API_KEY,\n baseURL: 'https://api.cerebras.ai/v1',\n fetch,\n });\n return { llm: new LLM({ model: CHAT_MODEL, client }), capturedRequests };\n}\n\nclass WeatherAgent extends voice.Agent {\n constructor() {\n super({\n instructions: 'You are a helpful assistant.',\n tools: {\n get_weather: llm.tool({\n description: 'Get the current weather for a location.',\n parameters: z.object({\n location: z.string().describe('The city name'),\n }),\n execute: async ({ location }) => {\n return `The weather in ${location} is sunny, 72°F.`;\n },\n }),\n },\n });\n }\n}\n\ndescribe('Cerebras', { timeout: 30_000 }, () => {\n it('basic chat completion returns a non-empty assistant message', async () => {\n const session = new voice.AgentSession({ llm: cerebrasLLM() });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n });\n\n it('LLM can invoke a tool and the result is returned', async () => {\n const session = new voice.AgentSession({ llm: new LLM({ model: TOOL_MODEL }) });\n await session.start({ agent: new WeatherAgent() });\n\n const result = session.run({ userInput: 'What is the weather in Tokyo?' });\n await result.wait();\n\n result.expect.nextEvent().isFunctionCall({\n name: 'get_weather',\n args: { location: 'Tokyo' },\n });\n result.expect.nextEvent().isFunctionCallOutput({\n output: JSON.stringify('The weather in Tokyo is sunny, 72°F.'),\n });\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n });\n\n it('gzip-only sends Content-Encoding: gzip with JSON content type', async () => {\n const { llm: model, capturedRequests } = cerebrasLLMWithCapture({\n useGzip: true,\n useMsgpack: false,\n });\n const session = new voice.AgentSession({ llm: model });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n\n const chatReqs = capturedRequests.filter((r) => r.url.includes('/chat/completions'));\n expect(chatReqs.length).toBeGreaterThan(0);\n expect(chatReqs[0]!.headers.get('content-type')).toBe('application/json');\n expect(chatReqs[0]!.headers.get('content-encoding')).toBe('gzip');\n });\n\n it('msgpack-only sends Content-Type: application/vnd.msgpack without gzip', async () => {\n const { llm: model, capturedRequests } = cerebrasLLMWithCapture({\n useGzip: false,\n useMsgpack: true,\n });\n const session = new voice.AgentSession({ llm: model });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n\n const chatReqs = capturedRequests.filter((r) => r.url.includes('/chat/completions'));\n expect(chatReqs.length).toBeGreaterThan(0);\n expect(chatReqs[0]!.headers.get('content-type')).toBe('application/vnd.msgpack');\n expect(chatReqs[0]!.headers.get('content-encoding')).toBeNull();\n });\n\n it('both flags send msgpack content type with gzip encoding', async () => {\n const { llm: model, capturedRequests } = cerebrasLLMWithCapture({\n useGzip: true,\n useMsgpack: true,\n });\n const session = new voice.AgentSession({ llm: model });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n\n const chatReqs = capturedRequests.filter((r) => r.url.includes('/chat/completions'));\n expect(chatReqs.length).toBeGreaterThan(0);\n expect(chatReqs[0]!.headers.get('content-type')).toBe('application/vnd.msgpack');\n expect(chatReqs[0]!.headers.get('content-encoding')).toBe('gzip');\n });\n\n it('with both flags off sends standard JSON without gzip', async () => {\n const session = new voice.AgentSession({\n llm: cerebrasLLM({ gzipCompression: false, msgpackEncoding: false }),\n });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n });\n\n it('streaming chat returns content via the LLM directly', async () => {\n const model = cerebrasLLM();\n const chatCtx = new llm.ChatContext();\n chatCtx.addMessage({ role: 'system', content: 'You are a helpful assistant.' });\n chatCtx.addMessage({ role: 'user', content: 'Count from 1 to 5.' });\n\n const stream = model.chat({ chatCtx });\n let text = '';\n for await (const chunk of stream) {\n if (chunk.delta?.content) {\n text += chunk.delta.content;\n }\n }\n\n expect(text.length).toBeGreaterThan(0);\n expect(text).toContain('3');\n });\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/llm.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { llm, voice } from '@livekit/agents';\nimport { encode } from '@msgpack/msgpack';\nimport { gzipSync } from 'node:zlib';\nimport OpenAI from 'openai';\nimport { assert, describe, expect, it } from 'vitest';\nimport { z } from 'zod';\nimport type { LLMOptions } from './llm.js';\nimport { LLM } from './llm.js';\n\nassert(process.env.CEREBRAS_API_KEY, 'CEREBRAS_API_KEY must be set');\n\n// llama3.1-8b is fast and has generous rate limits but can't do tool calls reliably;\n// qwen-3-235b is needed for function calling but has tight per-minute token quotas.\nconst CHAT_MODEL = 'llama3.1-8b';\nconst TOOL_MODEL = 'qwen-3-235b-a22b-instruct-2507';\n\ninterface CapturedRequest {\n url: string;\n headers: Headers;\n}\n\n/**\n * Wraps a real fetch, applying msgpack/gzip compression and capturing outgoing\n * request metadata for assertion. TypeScript equivalent of Python's\n * `HeaderCapturingTransport` + `_CerebrasClient`.\n */\nfunction createCapturingFetch(opts: { useMsgpack: boolean; useGzip: boolean }): {\n fetch: typeof globalThis.fetch;\n capturedRequests: CapturedRequest[];\n} {\n const capturedRequests: CapturedRequest[] = [];\n\n const fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n if (init?.method === 'POST' && init.body && typeof init.body === 'string') {\n const headers = new Headers(init.headers);\n\n let body: Uint8Array;\n if (opts.useMsgpack) {\n body = encode(JSON.parse(init.body));\n headers.set('Content-Type', 'application/vnd.msgpack');\n } else {\n body = new TextEncoder().encode(init.body);\n }\n\n if (opts.useGzip) {\n body = gzipSync(body, { level: 5 });\n headers.set('Content-Encoding', 'gzip');\n }\n\n capturedRequests.push({ url: extractUrl(input), headers });\n return globalThis.fetch(input, { ...init, body: Buffer.from(body), headers });\n }\n\n capturedRequests.push({ url: extractUrl(input), headers: new Headers(init?.headers) });\n return globalThis.fetch(input, init);\n };\n\n return { fetch: fetch as typeof globalThis.fetch, capturedRequests };\n}\n\nfunction extractUrl(input: RequestInfo | URL): string {\n if (typeof input === 'string') return input;\n if (input instanceof URL) return input.toString();\n return input.url;\n}\n\nfunction cerebrasLLM(opts: Partial<LLMOptions> = {}): LLM {\n return new LLM({ model: CHAT_MODEL, ...opts });\n}\n\nfunction cerebrasLLMWithCapture(opts: { useGzip: boolean; useMsgpack: boolean }): {\n llm: LLM;\n capturedRequests: CapturedRequest[];\n} {\n const { fetch, capturedRequests } = createCapturingFetch(opts);\n const client = new OpenAI({\n apiKey: process.env.CEREBRAS_API_KEY,\n baseURL: 'https://api.cerebras.ai/v1',\n fetch,\n });\n return { llm: new LLM({ model: CHAT_MODEL, client }), capturedRequests };\n}\n\nclass WeatherAgent extends voice.Agent {\n constructor() {\n super({\n instructions: 'You are a helpful assistant.',\n tools: [\n llm.tool({\n name: 'get_weather',\n description: 'Get the current weather for a location.',\n parameters: z.object({\n location: z.string().describe('The city name'),\n }),\n execute: async ({ location }) => {\n return `The weather in ${location} is sunny, 72°F.`;\n },\n }),\n ],\n });\n }\n}\n\ndescribe('Cerebras', { timeout: 30_000 }, () => {\n it('basic chat completion returns a non-empty assistant message', async () => {\n const session = new voice.AgentSession({ llm: cerebrasLLM() });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n });\n\n it('LLM can invoke a tool and the result is returned', async () => {\n const session = new voice.AgentSession({ llm: new LLM({ model: TOOL_MODEL }) });\n await session.start({ agent: new WeatherAgent() });\n\n const result = session.run({ userInput: 'What is the weather in Tokyo?' });\n await result.wait();\n\n result.expect.nextEvent().isFunctionCall({\n name: 'get_weather',\n args: { location: 'Tokyo' },\n });\n result.expect.nextEvent().isFunctionCallOutput({\n output: JSON.stringify('The weather in Tokyo is sunny, 72°F.'),\n });\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n });\n\n it('gzip-only sends Content-Encoding: gzip with JSON content type', async () => {\n const { llm: model, capturedRequests } = cerebrasLLMWithCapture({\n useGzip: true,\n useMsgpack: false,\n });\n const session = new voice.AgentSession({ llm: model });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n\n const chatReqs = capturedRequests.filter((r) => r.url.includes('/chat/completions'));\n expect(chatReqs.length).toBeGreaterThan(0);\n expect(chatReqs[0]!.headers.get('content-type')).toBe('application/json');\n expect(chatReqs[0]!.headers.get('content-encoding')).toBe('gzip');\n });\n\n it('msgpack-only sends Content-Type: application/vnd.msgpack without gzip', async () => {\n const { llm: model, capturedRequests } = cerebrasLLMWithCapture({\n useGzip: false,\n useMsgpack: true,\n });\n const session = new voice.AgentSession({ llm: model });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n\n const chatReqs = capturedRequests.filter((r) => r.url.includes('/chat/completions'));\n expect(chatReqs.length).toBeGreaterThan(0);\n expect(chatReqs[0]!.headers.get('content-type')).toBe('application/vnd.msgpack');\n expect(chatReqs[0]!.headers.get('content-encoding')).toBeNull();\n });\n\n it('both flags send msgpack content type with gzip encoding', async () => {\n const { llm: model, capturedRequests } = cerebrasLLMWithCapture({\n useGzip: true,\n useMsgpack: true,\n });\n const session = new voice.AgentSession({ llm: model });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n\n const chatReqs = capturedRequests.filter((r) => r.url.includes('/chat/completions'));\n expect(chatReqs.length).toBeGreaterThan(0);\n expect(chatReqs[0]!.headers.get('content-type')).toBe('application/vnd.msgpack');\n expect(chatReqs[0]!.headers.get('content-encoding')).toBe('gzip');\n });\n\n it('with both flags off sends standard JSON without gzip', async () => {\n const session = new voice.AgentSession({\n llm: cerebrasLLM({ gzipCompression: false, msgpackEncoding: false }),\n });\n await session.start({\n agent: new voice.Agent({ instructions: 'You are a helpful assistant.' }),\n });\n\n const result = session.run({ userInput: 'Say hello in exactly one word.' });\n await result.wait();\n\n result.expect.nextEvent().isMessage({ role: 'assistant' });\n result.expect.noMoreEvents();\n\n await session.close();\n });\n\n it('streaming chat returns content via the LLM directly', async () => {\n const model = cerebrasLLM();\n const chatCtx = new llm.ChatContext();\n chatCtx.addMessage({ role: 'system', content: 'You are a helpful assistant.' });\n chatCtx.addMessage({ role: 'user', content: 'Count from 1 to 5.' });\n\n const stream = model.chat({ chatCtx });\n let text = '';\n for await (const chunk of stream) {\n if (chunk.delta?.content) {\n text += chunk.delta.content;\n }\n }\n\n expect(text.length).toBeGreaterThan(0);\n expect(text).toContain('3');\n });\n});\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livekit/agents-plugin-cerebras",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Cerebras plugin for LiveKit Node Agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"require": "dist/index.cjs",
|
|
@@ -25,23 +25,23 @@
|
|
|
25
25
|
"README.md"
|
|
26
26
|
],
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@livekit/rtc-node": "^0.13.
|
|
28
|
+
"@livekit/rtc-node": "^0.13.30",
|
|
29
29
|
"@microsoft/api-extractor": "^7.35.0",
|
|
30
30
|
"tsup": "^8.3.5",
|
|
31
31
|
"typescript": "^5.0.0",
|
|
32
32
|
"zod": "^3.25.76 || ^4.1.8",
|
|
33
|
-
"@livekit/agents": "1.
|
|
34
|
-
"@livekit/agents-plugin-openai": "1.
|
|
35
|
-
"@livekit/agents-plugins-test": "1.
|
|
33
|
+
"@livekit/agents": "1.5.0",
|
|
34
|
+
"@livekit/agents-plugin-openai": "1.5.0",
|
|
35
|
+
"@livekit/agents-plugins-test": "1.5.0"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@msgpack/msgpack": "^3.0.0",
|
|
39
39
|
"openai": "^6.8.1"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@livekit/rtc-node": "^0.13.
|
|
43
|
-
"@livekit/agents": "1.
|
|
44
|
-
"@livekit/agents-plugin-openai": "1.
|
|
42
|
+
"@livekit/rtc-node": "^0.13.30",
|
|
43
|
+
"@livekit/agents": "1.5.0",
|
|
44
|
+
"@livekit/agents-plugin-openai": "1.5.0"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsup --onSuccess \"pnpm build:types\"",
|
package/src/llm.test.ts
CHANGED
|
@@ -88,8 +88,9 @@ class WeatherAgent extends voice.Agent {
|
|
|
88
88
|
constructor() {
|
|
89
89
|
super({
|
|
90
90
|
instructions: 'You are a helpful assistant.',
|
|
91
|
-
tools:
|
|
92
|
-
|
|
91
|
+
tools: [
|
|
92
|
+
llm.tool({
|
|
93
|
+
name: 'get_weather',
|
|
93
94
|
description: 'Get the current weather for a location.',
|
|
94
95
|
parameters: z.object({
|
|
95
96
|
location: z.string().describe('The city name'),
|
|
@@ -98,7 +99,7 @@ class WeatherAgent extends voice.Agent {
|
|
|
98
99
|
return `The weather in ${location} is sunny, 72°F.`;
|
|
99
100
|
},
|
|
100
101
|
}),
|
|
101
|
-
|
|
102
|
+
],
|
|
102
103
|
});
|
|
103
104
|
}
|
|
104
105
|
}
|