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
25 changes: 11 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@openrouter/sdk",
"version": "0.3.15",
"version": "0.3.11",
"author": "OpenRouter",
"description": "The OpenRouter TypeScript SDK is a type-safe toolkit for building AI applications with access to 300+ language models through a unified API.",
"keywords": [
Expand Down Expand Up @@ -64,19 +64,17 @@
"type": "git",
"url": "https://github.com/OpenRouterTeam/typescript-sdk.git"
},
"scripts": {
"lint": "eslint --cache --max-warnings=0 src",
"build": "tsc",
"typecheck": "tsc --noEmit",
"prepublishOnly": "npm run build",
"postinstall": "node scripts/check-types.js || true",
"test": "vitest --run --project unit",
"test:e2e": "vitest --run --project e2e",
"test:watch": "vitest --watch --project unit"
},
"peerDependencies": {},
"scripts": {
"lint": "eslint --cache --max-warnings=0 src",
"build": "tsc",
"typecheck": "tsc --noEmit",
"prepublishOnly": "npm run build",
"test": "vitest --run",
"test:watch": "vitest"
},
"devDependencies": {
"@eslint/js": "^9.19.0",
"@linear/sdk": "^69.0.0",
"@types/node": "^22.13.12",
"dotenv": "^16.4.7",
"eslint": "^9.19.0",
Expand All @@ -87,6 +85,5 @@
},
"dependencies": {
"zod": "^3.25.0 || ^4.0.0"
},
"packageManager": "pnpm@10.22.0"
}
}
28 changes: 28 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

83 changes: 83 additions & 0 deletions tests/e2e/call-model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,89 @@ describe('callModel E2E Tests', () => {
expect(fullToolCall.length).toBeGreaterThan(0);
}
}, 30000);

it('should stream preliminary results from generator tools via getToolStream', async () => {
const progressTool = {
type: ToolType.Function,
function: {
name: 'search_with_progress',
description: 'Search with progress updates',
inputSchema: z.object({
query: z.string().describe('Search query'),
}),
eventSchema: z.object({
status: z.string(),
progress: z.number().min(0).max(100),
}),
outputSchema: z.object({
results: z.array(z.string()),
totalFound: z.number(),
}),
execute: async function* (params: { query: string }) {
// Preliminary event 1
yield { status: 'Starting search...', progress: 0 };

await new Promise((r) => setTimeout(r, 100));

// Preliminary event 2
yield { status: 'Searching...', progress: 50 };

await new Promise((r) => setTimeout(r, 100));

// Preliminary event 3
yield { status: 'Almost done...', progress: 90 };

// Final result
yield {
results: [`Found: ${params.query}`],
totalFound: 1,
};
},
},
};

const response = client.callModel({
model: 'anthropic/claude-haiku-4.5',
input: fromChatMessages([
{
role: 'user',
content: 'Search for TypeScript documentation using the search_with_progress tool.',
},
]),
tools: [progressTool],
toolChoice: 'required',
stopWhen: stepCountIs(1), // Limit to single turn to ensure one tool call
});

const preliminaryResults: Array<{ status: string; progress: number }> = [];
let toolCallId: string | undefined;

for await (const event of response.getToolStream()) {
if (event.type === 'preliminary_result') {
if (!toolCallId) {
toolCallId = event.toolCallId;
}
expect(event.toolCallId).toBe(toolCallId); // Same tool call
preliminaryResults.push(event.result as { status: string; progress: number });
}
}

// Verify preliminary results were received
expect(preliminaryResults.length).toBeGreaterThanOrEqual(3);

// Verify events came in order (progress should be increasing)
for (let i = 1; i < preliminaryResults.length; i++) {
const current = preliminaryResults[i];
const previous = preliminaryResults[i - 1];
if (current && previous) {
expect(current.progress).toBeGreaterThanOrEqual(previous.progress);
}
}

// Verify final response
const finalResponse = await response.getResponse();
expect(finalResponse).toBeDefined();
}, 60000);
});

describe('response.fullResponsesStream - Streaming all events', () => {
Expand Down
Loading