Skip to content
Open
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
107 changes: 107 additions & 0 deletions apps/vscode-e2e/fixtures/read-file.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
{
"fixtures": [
{
"match": {
"userMessage": "READ_FILE_SIMPLE_SMOKE"
},
"response": {
"toolCalls": [
{
"name": "read_file",
"arguments": "{\"path\":\"simple-read-file-smoke.txt\",\"mode\":\"slice\",\"offset\":1,\"limit\":2000,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":2000}}",
"id": "call_read_file_simple_001"
}
]
}
},
{
"match": {
"userMessage": "READ_FILE_MULTILINE_SMOKE"
},
"response": {
"toolCalls": [
{
"name": "read_file",
"arguments": "{\"path\":\"multiline-read-file.txt\",\"mode\":\"slice\",\"offset\":1,\"limit\":20,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":20}}",
"id": "call_read_file_multiline_001"
}
]
}
},
{
"match": {
"userMessage": "READ_FILE_SLICE_SMOKE"
},
"response": {
"toolCalls": [
{
"name": "read_file",
"arguments": "{\"path\":\"multiline-read-file.txt\",\"mode\":\"slice\",\"offset\":2,\"limit\":3,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":2000}}",
"id": "call_read_file_slice_001"
}
]
}
},
{
"match": {
"userMessage": "READ_FILE_MISSING_SMOKE"
},
"response": {
"toolCalls": [
{
"name": "read_file",
"arguments": "{\"path\":\"non-existent-read-file.txt\",\"mode\":\"slice\",\"offset\":1,\"limit\":2000,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":2000}}",
"id": "call_read_file_missing_001"
}
]
}
},
{
"match": {
"userMessage": "READ_FILE_XML_SMOKE"
},
"response": {
"toolCalls": [
{
"name": "read_file",
"arguments": "{\"path\":\"xml-content-read-file.xml\",\"mode\":\"slice\",\"offset\":1,\"limit\":50,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":50}}",
"id": "call_read_file_xml_001"
}
]
}
},
{
"match": {
"userMessage": "READ_FILE_MULTIPLE_SMOKE"
},
"response": {
"toolCalls": [
{
"name": "read_file",
"arguments": "{\"path\":\"simple-read-file-smoke.txt\",\"mode\":\"slice\",\"offset\":1,\"limit\":20,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":20}}",
"id": "call_read_file_multiple_simple_001"
},
{
"name": "read_file",
"arguments": "{\"path\":\"multiline-read-file.txt\",\"mode\":\"slice\",\"offset\":1,\"limit\":20,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":20}}",
"id": "call_read_file_multiple_multiline_001"
}
]
}
},
{
"match": {
"userMessage": "READ_FILE_LARGE_SMOKE"
},
"response": {
"toolCalls": [
{
"name": "read_file",
"arguments": "{\"path\":\"large-read-file.txt\",\"mode\":\"slice\",\"offset\":1,\"limit\":1000,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":2000}}",
"id": "call_read_file_large_001"
}
]
}
}
]
}
114 changes: 114 additions & 0 deletions apps/vscode-e2e/src/fixtures/read-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { LLMock } from "@copilotkit/aimock"
import type { ChatCompletionRequest, ChatMessage } from "@copilotkit/aimock"

type ToolResultExpectation = { toolCallId: string; expected: string[] }

type ReadFileResultFixture = {
toolCallId: string
expected: string[] | ToolResultExpectation[]
result: string
id: string
}

function isToolResultExpectation(value: unknown): value is ToolResultExpectation {
return typeof value === "object" && value !== null && "toolCallId" in value && "expected" in value
}

function toolResultContains(req: ChatCompletionRequest, toolCallId: string, expected: string[]) {
const messages = Array.isArray(req?.messages) ? req.messages : []
const toolMessage = messages.find(
(message: ChatMessage) => message?.role === "tool" && message.tool_call_id === toolCallId,
)

const content = toolMessage?.content
if (typeof content !== "string") {
return false
}

return expected.every((text) => content.includes(text))
}

function toolResultsContain(req: ChatCompletionRequest, expectations: ToolResultExpectation[]) {
return expectations.every(({ toolCallId, expected }) => toolResultContains(req, toolCallId, expected))
}

export function addReadFileResultFixtures(mock: InstanceType<typeof LLMock>) {
const fixtures: ReadFileResultFixture[] = [
{
toolCallId: "call_read_file_simple_001",
expected: ["File: simple-read-file-smoke.txt", "1 | Hello, World!"],
result: 'The file [`simple-read-file-smoke.txt`](simple-read-file-smoke.txt) contains the text: "Hello, World!"',
id: "call_read_file_simple_002",
},
{
toolCallId: "call_read_file_multiline_001",
expected: ["File: multiline-read-file.txt", "1 | Line 1", "5 | Line 5"],
result: "The file [`multiline-read-file.txt`](multiline-read-file.txt) contains 5 lines: Line 1, Line 2, Line 3, Line 4, and Line 5.",
id: "call_read_file_multiline_002",
},
{
toolCallId: "call_read_file_slice_001",
expected: ["File: multiline-read-file.txt", "2 | Line 2", "3 | Line 3", "4 | Line 4"],
result: "The three lines read from [`multiline-read-file.txt`](multiline-read-file.txt) starting at offset 2 are:\n\n- Line 2\n- Line 3\n- Line 4",
id: "call_read_file_slice_002",
},
{
toolCallId: "call_read_file_missing_001",
expected: ["non-existent-read-file.txt", "ENOENT", "no such file or directory"],
result: "Attempting to read [`non-existent-read-file.txt`](non-existent-read-file.txt) resulted in an error: the file does not exist. This error was handled appropriately and no file contents were returned.",
id: "call_read_file_missing_002",
},
{
toolCallId: "call_read_file_xml_001",
expected: ["File: xml-content-read-file.xml", "<child>Test content</child>", "<data>Some data</data>"],
result: "The XML file [`xml-content-read-file.xml`](xml-content-read-file.xml) contains the following elements:\n- `<root>` (root element)\n- `<child>` (child of root)\n- `<data>` (child of root)\n\nThe structure is:\n```xml\n<root>\n <child>Test content</child>\n <data>Some data</data>\n</root>\n```",
id: "call_read_file_xml_002",
},
{
toolCallId: "call_read_file_multiple_multiline_001",
expected: [
{
toolCallId: "call_read_file_multiple_simple_001",
expected: ["File: simple-read-file-smoke.txt", "1 | Hello, World!"],
},
{
toolCallId: "call_read_file_multiple_multiline_001",
expected: ["File: multiline-read-file.txt", "1 | Line 1", "5 | Line 5"],
},
],
result: "Contents of [`simple-read-file-smoke.txt`](simple-read-file-smoke.txt):\n\n```\nHello, World!\n```\n\nContents of [`multiline-read-file.txt`](multiline-read-file.txt):\n\n```\nLine 1\nLine 2\nLine 3\nLine 4\nLine 5\n```",
id: "call_read_file_multiple_002",
},
{
toolCallId: "call_read_file_large_001",
expected: [
"File: large-read-file.txt",
"1 | Line 1: This is a test line with some content",
"100 | Line 100: This is a test line with some content",
],
result: "The file [`large-read-file.txt`](large-read-file.txt) contains 100 lines, each following the pattern: `Line N: This is a test line with some content`, where `N` is the line number (from 1 to 100). The structure is consistent throughout the file, with only the line number changing on each line.",
id: "call_read_file_large_002",
},
]

for (const fixture of fixtures) {
mock.addFixture({
match: {
toolCallId: fixture.toolCallId,
predicate: (req) =>
isToolResultExpectation(fixture.expected[0])
? toolResultsContain(req, fixture.expected as ToolResultExpectation[])
: toolResultContains(req, fixture.toolCallId, fixture.expected as string[]),
},
response: {
toolCalls: [
{
name: "attempt_completion",
arguments: JSON.stringify({ result: fixture.result }),
id: fixture.id,
},
],
},
})
}
}
4 changes: 4 additions & 0 deletions apps/vscode-e2e/src/runTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import * as fs from "fs/promises"
import { runTests } from "@vscode/test-electron"
import { LLMock } from "@copilotkit/aimock"

import { addReadFileResultFixtures } from "./fixtures/read-file"

async function main() {
const isRecord = process.env.AIMOCK_RECORD === "true"

Expand Down Expand Up @@ -55,6 +57,8 @@ async function main() {
mock.loadFixtureDir(fixturesDir)

if (!isRecord) {
addReadFileResultFixtures(mock)

// The modes test (switch_mode → ask) triggers a second API call whose last
// user message starts with <environment_details> directly — no <user_message>
// wrapper. JSON fixtures use substring matching so a bare "<environment_details>"
Expand Down
Loading
Loading