Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 43 additions & 120 deletions src/api/providers/__tests__/friendli.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { friendliDefaultModelId, friendliModels } from "@roo-code/types"
import { buildApiHandler } from "../../index"
import { getModelMaxOutputTokens } from "../../../shared/api"
import { FriendliHandler } from "../friendli"
import { asyncStreamFrom, collectStream } from "../../../test-utils/stream"

// Create mock functions
const mockCreate = vi.fn()
Expand All @@ -31,18 +32,18 @@ describe("FriendliHandler", () => {
beforeEach(() => {
vi.clearAllMocks()
// Set up default mock implementation
mockCreate.mockImplementation(async () => ({
[Symbol.asyncIterator]: async function* () {
yield {
mockCreate.mockImplementation(async () =>
asyncStreamFrom([
{
choices: [
{
delta: { content: "Test response" },
index: 0,
},
],
usage: null,
}
yield {
},
{
choices: [
{
delta: {},
Expand All @@ -54,9 +55,9 @@ describe("FriendliHandler", () => {
completion_tokens: 5,
total_tokens: 15,
},
}
},
}))
},
]),
)
handler = new FriendliHandler({ friendliApiKey: "test-key" })
})

Expand Down Expand Up @@ -189,19 +190,7 @@ describe("FriendliHandler", () => {
it("createMessage should yield text content from stream", async () => {
const testContent = "This is test content from Friendli stream"

mockCreate.mockImplementationOnce(() => {
return {
[Symbol.asyncIterator]: () => ({
next: vi
.fn()
.mockResolvedValueOnce({
done: false,
value: { choices: [{ delta: { content: testContent } }] },
})
.mockResolvedValueOnce({ done: true }),
}),
}
})
mockCreate.mockImplementationOnce(() => asyncStreamFrom([{ choices: [{ delta: { content: testContent } }] }]))

const stream = handler.createMessage("system prompt", [])
const firstChunk = await stream.next()
Expand All @@ -211,19 +200,9 @@ describe("FriendliHandler", () => {
})

it("createMessage should yield usage data from stream", async () => {
mockCreate.mockImplementationOnce(() => {
return {
[Symbol.asyncIterator]: () => ({
next: vi
.fn()
.mockResolvedValueOnce({
done: false,
value: { choices: [{ delta: {} }], usage: { prompt_tokens: 10, completion_tokens: 20 } },
})
.mockResolvedValueOnce({ done: true }),
}),
}
})
mockCreate.mockImplementationOnce(() =>
asyncStreamFrom([{ choices: [{ delta: {} }], usage: { prompt_tokens: 10, completion_tokens: 20 } }]),
)

const stream = handler.createMessage("system prompt", [])
const firstChunk = await stream.next()
Expand All @@ -240,15 +219,7 @@ describe("FriendliHandler", () => {
friendliApiKey: "test-friendli-api-key",
})

mockCreate.mockImplementationOnce(() => {
return {
[Symbol.asyncIterator]: () => ({
async next() {
return { done: true }
},
}),
}
})
mockCreate.mockImplementationOnce(() => asyncStreamFrom([]))

const systemPrompt = "Test system prompt for Friendli"
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Test message for Friendli" }]
Expand Down Expand Up @@ -276,13 +247,7 @@ describe("FriendliHandler", () => {
modelTemperature: 0.3,
})

mockCreate.mockImplementationOnce(() => ({
[Symbol.asyncIterator]: () => ({
async next() {
return { done: true }
},
}),
}))
mockCreate.mockImplementationOnce(() => asyncStreamFrom([]))

const messageGenerator = handlerWithModel.createMessage("system", [])
await messageGenerator.next()
Expand All @@ -308,27 +273,27 @@ describe("FriendliHandler", () => {
})

it("createMessage should handle stream with multiple chunks", async () => {
mockCreate.mockImplementationOnce(async () => ({
[Symbol.asyncIterator]: async function* () {
yield {
mockCreate.mockImplementationOnce(async () =>
asyncStreamFrom([
{
choices: [
{
delta: { content: "Hello" },
index: 0,
},
],
usage: null,
}
yield {
},
{
choices: [
{
delta: { content: " world" },
index: 0,
},
],
usage: null,
}
yield {
},
{
choices: [
{
delta: {},
Expand All @@ -340,18 +305,15 @@ describe("FriendliHandler", () => {
completion_tokens: 10,
total_tokens: 15,
},
}
},
}))
},
]),
)

const systemPrompt = "You are a helpful assistant."
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hi" }]

const stream = handler.createMessage(systemPrompt, messages)
const chunks = []
for await (const chunk of stream) {
chunks.push(chunk)
}
const chunks = await collectStream(stream)

expect(chunks[0]).toEqual({ type: "text", text: "Hello" })
expect(chunks[1]).toEqual({ type: "text", text: " world" })
Expand Down Expand Up @@ -417,13 +379,7 @@ describe("FriendliHandler — Friendli-specific reasoning params", () => {
reasoningEffort: "high",
})

mockCreate.mockImplementationOnce(() => ({
[Symbol.asyncIterator]: () => ({
async next() {
return { done: true }
},
}),
}))
mockCreate.mockImplementationOnce(() => asyncStreamFrom([]))

await handler.createMessage("system", []).next()

Expand All @@ -446,13 +402,7 @@ describe("FriendliHandler — Friendli-specific reasoning params", () => {
enableReasoningEffort: false,
})

mockCreate.mockImplementationOnce(() => ({
[Symbol.asyncIterator]: () => ({
async next() {
return { done: true }
},
}),
}))
mockCreate.mockImplementationOnce(() => asyncStreamFrom([]))

await handler.createMessage("system", []).next()

Expand All @@ -471,13 +421,7 @@ describe("FriendliHandler — Friendli-specific reasoning params", () => {
reasoningEffort: "none",
})

mockCreate.mockImplementationOnce(() => ({
[Symbol.asyncIterator]: () => ({
async next() {
return { done: true }
},
}),
}))
mockCreate.mockImplementationOnce(() => asyncStreamFrom([]))

await handler.createMessage("system", []).next()

Expand All @@ -496,13 +440,7 @@ describe("FriendliHandler — Friendli-specific reasoning params", () => {
reasoningEffort: "disable",
})

mockCreate.mockImplementationOnce(() => ({
[Symbol.asyncIterator]: () => ({
async next() {
return { done: true }
},
}),
}))
mockCreate.mockImplementationOnce(() => asyncStreamFrom([]))

await handler.createMessage("system", []).next()

Expand All @@ -520,13 +458,7 @@ describe("FriendliHandler — Friendli-specific reasoning params", () => {
// No enableReasoningEffort or reasoningEffort — model default "high" kicks in
})

mockCreate.mockImplementationOnce(() => ({
[Symbol.asyncIterator]: () => ({
async next() {
return { done: true }
},
}),
}))
mockCreate.mockImplementationOnce(() => asyncStreamFrom([]))

await handler.createMessage("system", []).next()

Expand All @@ -545,13 +477,7 @@ describe("FriendliHandler — Friendli-specific reasoning params", () => {
reasoningEffort: "high",
})

mockCreate.mockImplementationOnce(() => ({
[Symbol.asyncIterator]: () => ({
async next() {
return { done: true }
},
}),
}))
mockCreate.mockImplementationOnce(() => asyncStreamFrom([]))

await handler.createMessage("system", []).next()

Expand All @@ -569,28 +495,25 @@ describe("FriendliHandler — Friendli-specific reasoning params", () => {
reasoningEffort: "high",
})

mockCreate.mockImplementationOnce(async () => ({
[Symbol.asyncIterator]: async function* () {
yield {
mockCreate.mockImplementationOnce(async () =>
asyncStreamFrom([
{
choices: [{ delta: { reasoning_content: "Let me think..." } }],
usage: null,
}
yield {
},
{
choices: [{ delta: { content: "The answer is 42" } }],
usage: null,
}
yield {
},
{
choices: [{ delta: {} }],
usage: { prompt_tokens: 10, completion_tokens: 20, total_tokens: 30 },
}
},
}))
},
]),
)

const stream = handler.createMessage("system", [])
const chunks = []
for await (const chunk of stream) {
chunks.push(chunk)
}
const chunks = await collectStream(stream)

expect(chunks).toContainEqual({ type: "reasoning", text: "Let me think..." })
expect(chunks).toContainEqual({ type: "text", text: "The answer is 42" })
Expand Down
Loading
Loading