mirror of https://github.com/buster-so/buster.git
make shared materialize function
This commit is contained in:
parent
fca5a645ae
commit
a3c9ce8900
|
@ -1,20 +1,8 @@
|
|||
import { type Sandbox, createSandbox } from '@buster/sandbox';
|
||||
import { materialize } from '@buster/test-utils';
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||
import { createBashTool } from './bash-tool';
|
||||
|
||||
async function materialize<T>(value: T | AsyncIterable<T>): Promise<T> {
|
||||
const asyncIterator = (value as any)?.[Symbol.asyncIterator];
|
||||
if (typeof asyncIterator === 'function') {
|
||||
let lastChunk: T | undefined;
|
||||
for await (const chunk of value as AsyncIterable<T>) {
|
||||
lastChunk = chunk;
|
||||
}
|
||||
if (lastChunk === undefined) throw new Error('Stream yielded no values');
|
||||
return lastChunk;
|
||||
}
|
||||
return value as T;
|
||||
}
|
||||
|
||||
describe.sequential('bash-tool integration test', () => {
|
||||
const hasApiKey = !!process.env.DAYTONA_API_KEY;
|
||||
let sharedSandbox: Sandbox;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { materialize } from '@buster/test-utils';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { createBashTool } from './bash-tool';
|
||||
|
||||
|
@ -10,19 +11,6 @@ vi.mock('braintrust', () => ({
|
|||
wrapTraced: vi.fn((fn) => fn),
|
||||
}));
|
||||
|
||||
async function materialize<T>(value: T | AsyncIterable<T>): Promise<T> {
|
||||
const asyncIterator = (value as any)?.[Symbol.asyncIterator];
|
||||
if (typeof asyncIterator === 'function') {
|
||||
let lastChunk: T | undefined;
|
||||
for await (const chunk of value as AsyncIterable<T>) {
|
||||
lastChunk = chunk;
|
||||
}
|
||||
if (lastChunk === undefined) throw new Error('Stream yielded no values');
|
||||
return lastChunk;
|
||||
}
|
||||
return value as T;
|
||||
}
|
||||
|
||||
describe('createBashTool', () => {
|
||||
const mockSandbox = {
|
||||
id: 'test-sandbox',
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { materialize } from '@buster/test-utils';
|
||||
import type { ToolCallOptions } from 'ai';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { createCreateDashboardsTool } from './create-dashboards-tool';
|
||||
|
@ -16,20 +17,6 @@ vi.mock('./create-dashboards-execute', () => ({
|
|||
createCreateDashboardsExecute: vi.fn(() => vi.fn()),
|
||||
}));
|
||||
|
||||
// Helper to handle tools that may return a value or an AsyncIterable of values
|
||||
async function materialize<T>(value: T | AsyncIterable<T>): Promise<T> {
|
||||
const asyncIterator = (value as any)?.[Symbol.asyncIterator];
|
||||
if (typeof asyncIterator === 'function') {
|
||||
let lastChunk: T | undefined;
|
||||
for await (const chunk of value as AsyncIterable<T>) {
|
||||
lastChunk = chunk;
|
||||
}
|
||||
if (lastChunk === undefined) throw new Error('Stream yielded no values');
|
||||
return lastChunk;
|
||||
}
|
||||
return value as T;
|
||||
}
|
||||
|
||||
describe('create-dashboards-tool streaming integration', () => {
|
||||
let context: CreateDashboardsContext;
|
||||
let updateMessageEntries: ReturnType<typeof vi.fn>;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { updateMessageEntries } from '@buster/database';
|
||||
import { materialize } from '@buster/test-utils';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { createModifyDashboardsTool } from './modify-dashboards-tool';
|
||||
import type { ModifyDashboardsContext, ModifyDashboardsInput } from './modify-dashboards-tool';
|
||||
|
@ -11,19 +12,6 @@ vi.mock('@buster/database', () => ({
|
|||
metricFilesToDashboardFiles: {},
|
||||
}));
|
||||
|
||||
async function materialize<T>(value: T | AsyncIterable<T>): Promise<T> {
|
||||
const asyncIterator = (value as any)?.[Symbol.asyncIterator];
|
||||
if (typeof asyncIterator === 'function') {
|
||||
let lastChunk: T | undefined;
|
||||
for await (const chunk of value as AsyncIterable<T>) {
|
||||
lastChunk = chunk;
|
||||
}
|
||||
if (lastChunk === undefined) throw new Error('Stream yielded no values');
|
||||
return lastChunk;
|
||||
}
|
||||
return value as T;
|
||||
}
|
||||
|
||||
// Mock the execute function directly since it's called internally
|
||||
vi.mock('./modify-dashboards-execute', () => ({
|
||||
createModifyDashboardsExecute: vi.fn(() =>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { updateMessageFields } from '@buster/database';
|
||||
import { materialize } from '@buster/test-utils';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { ModifyMetricsContext, ModifyMetricsInput } from './modify-metrics-tool';
|
||||
import { createModifyMetricsTool } from './modify-metrics-tool';
|
||||
|
@ -12,19 +13,6 @@ vi.mock('./modify-metrics-execute', () => ({
|
|||
createModifyMetricsExecute: vi.fn(() => vi.fn()),
|
||||
}));
|
||||
|
||||
async function materialize<T>(value: T | AsyncIterable<T>): Promise<T> {
|
||||
const asyncIterator = (value as any)?.[Symbol.asyncIterator];
|
||||
if (typeof asyncIterator === 'function') {
|
||||
let lastChunk: T | undefined;
|
||||
for await (const chunk of value as AsyncIterable<T>) {
|
||||
lastChunk = chunk;
|
||||
}
|
||||
if (lastChunk === undefined) throw new Error('Stream yielded no values');
|
||||
return lastChunk;
|
||||
}
|
||||
return value as T;
|
||||
}
|
||||
|
||||
describe('modify-metrics-tool streaming integration', () => {
|
||||
let context: ModifyMetricsContext;
|
||||
const mockToolCallOptions = {
|
||||
|
|
|
@ -9,3 +9,6 @@ export * from './envHelpers';
|
|||
|
||||
// Mock helpers
|
||||
export * from './mock-helpers';
|
||||
|
||||
// Streaming utilities
|
||||
export * from './streaming-utils';
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* Streaming test utilities for handling AsyncIterable values in tests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helper to handle tools that may return a value or an AsyncIterable of values.
|
||||
* For AsyncIterable values, it materializes the stream by consuming all chunks
|
||||
* and returning the last one.
|
||||
*
|
||||
* @param value - The value to materialize, either a direct value or an AsyncIterable
|
||||
* @returns Promise resolving to the materialized value
|
||||
* @throws Error if the stream yields no values
|
||||
*/
|
||||
export async function materialize<T>(value: T | AsyncIterable<T>): Promise<T> {
|
||||
// biome-ignore lint/suspicious/noExplicitAny: we are ignoring this for now
|
||||
const asyncIterator = (value as any)?.[Symbol.asyncIterator];
|
||||
if (typeof asyncIterator === 'function') {
|
||||
let lastChunk: T | undefined;
|
||||
for await (const chunk of value as AsyncIterable<T>) {
|
||||
lastChunk = chunk;
|
||||
}
|
||||
if (lastChunk === undefined) throw new Error('Stream yielded no values');
|
||||
return lastChunk;
|
||||
}
|
||||
return value as T;
|
||||
}
|
Loading…
Reference in New Issue