From bea220934b8d11d97e35599dbc68ef3e69bb9523 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Sun, 31 Aug 2025 21:55:30 +0000
Subject: [PATCH 1/4] feat(api): update via SDK Studio
---
.github/workflows/create-releases.yml | 33 --
.github/workflows/release-doctor.yml | 1 -
.stats.yml | 6 +-
README.md | 22 +-
api.md | 48 --
bin/check-release-environment | 4 -
scripts/detect-breaking-changes | 8 +-
src/client.ts | 8 -
src/core/streaming.ts | 315 ------------
src/internal/decoders/line.ts | 135 -----
src/internal/parse.ts | 14 -
src/internal/request-options.ts | 2 -
src/resources/beta/beta.ts | 146 +-----
src/resources/beta/index.ts | 27 +-
src/resources/beta/task-group.ts | 267 +---------
src/resources/beta/task-run.ts | 514 +-------------------
src/resources/index.ts | 3 -
src/resources/shared.ts | 44 +-
src/resources/task-run.ts | 241 ++++-----
src/streaming.ts | 2 -
tests/api-resources/beta/beta.test.ts | 21 -
tests/api-resources/beta/task-group.test.ts | 126 -----
tests/api-resources/beta/task-run.test.ts | 80 ---
tests/api-resources/task-run.test.ts | 37 +-
tests/internal/decoders/line.test.ts | 128 -----
tests/streaming.test.ts | 219 ---------
26 files changed, 141 insertions(+), 2310 deletions(-)
delete mode 100644 .github/workflows/create-releases.yml
delete mode 100644 src/core/streaming.ts
delete mode 100644 src/internal/decoders/line.ts
delete mode 100644 src/streaming.ts
delete mode 100644 tests/api-resources/beta/beta.test.ts
delete mode 100644 tests/api-resources/beta/task-group.test.ts
delete mode 100644 tests/api-resources/beta/task-run.test.ts
delete mode 100644 tests/internal/decoders/line.test.ts
delete mode 100644 tests/streaming.test.ts
diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml
deleted file mode 100644
index a517c9c..0000000
--- a/.github/workflows/create-releases.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-name: Create releases
-on:
- schedule:
- - cron: '0 5 * * *' # every day at 5am UTC
- push:
- branches:
- - main
-
-jobs:
- release:
- name: release
- if: github.ref == 'refs/heads/main' && github.repository == 'parallel-web/parallel-sdk-typescript'
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v4
-
- - uses: stainless-api/trigger-release-please@v1
- id: release
- with:
- repo: ${{ github.event.repository.full_name }}
- stainless-api-key: ${{ secrets.STAINLESS_API_KEY }}
-
- - name: Set up Node
- if: ${{ steps.release.outputs.releases_created }}
- uses: actions/setup-node@v3
- with:
- node-version: '20'
-
- - name: Install dependencies
- if: ${{ steps.release.outputs.releases_created }}
- run: |
- yarn install
diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml
index fe84245..6ccfd4d 100644
--- a/.github/workflows/release-doctor.yml
+++ b/.github/workflows/release-doctor.yml
@@ -18,4 +18,3 @@ jobs:
run: |
bash ./bin/check-release-environment
env:
- STAINLESS_API_KEY: ${{ secrets.STAINLESS_API_KEY }}
diff --git a/.stats.yml b/.stats.yml
index 57243e7..c703e97 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 12
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/parallel-web%2Fparallel-sdk-1aeb1c81a84999f2d27ca9e86b041d74b892926bed126dc9b0f3cff4d7b26963.yml
-openapi_spec_hash: 6280f6c6fb537f7c9ac5cc33ee2e433d
+configured_endpoints: 3
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/parallel-web%2Fparallel-sdk-ff0d5939e135b67b3448abf72d8bb0f9a574194337c7c7192453781347a9601d.yml
+openapi_spec_hash: f3ce85349af6273a671d3d2781c4c877
config_hash: 284b51e02bda8519b1f21bb67f1809e0
diff --git a/README.md b/README.md
index 20d1935..6dc066d 100644
--- a/README.md
+++ b/README.md
@@ -29,10 +29,7 @@ const client = new Parallel({
apiKey: process.env['PARALLEL_API_KEY'], // This is the default and can be omitted
});
-const taskRun = await client.taskRun.create({
- input: 'What was the GDP of France in 2023?',
- processor: 'base',
-});
+const taskRun = await client.taskRun.create({ input: 'France (2023)', processor: 'processor' });
console.log(taskRun.run_id);
```
@@ -49,10 +46,7 @@ const client = new Parallel({
apiKey: process.env['PARALLEL_API_KEY'], // This is the default and can be omitted
});
-const params: Parallel.TaskRunCreateParams = {
- input: 'What was the GDP of France in 2023?',
- processor: 'base',
-};
+const params: Parallel.TaskRunCreateParams = { input: 'France (2023)', processor: 'processor' };
const taskRun: Parallel.TaskRun = await client.taskRun.create(params);
```
@@ -67,7 +61,7 @@ a subclass of `APIError` will be thrown:
```ts
const taskRun = await client.taskRun
- .create({ input: 'What was the GDP of France in 2023?', processor: 'base' })
+ .create({ input: 'France (2023)', processor: 'processor' })
.catch(async (err) => {
if (err instanceof Parallel.APIError) {
console.log(err.status); // 400
@@ -108,7 +102,7 @@ const client = new Parallel({
});
// Or, configure per-request:
-await client.taskRun.create({ input: 'What was the GDP of France in 2023?', processor: 'base' }, {
+await client.taskRun.create({ input: 'France (2023)', processor: 'processor' }, {
maxRetries: 5,
});
```
@@ -125,7 +119,7 @@ const client = new Parallel({
});
// Override per-request:
-await client.taskRun.create({ input: 'What was the GDP of France in 2023?', processor: 'base' }, {
+await client.taskRun.create({ input: 'France (2023)', processor: 'processor' }, {
timeout: 5 * 1000,
});
```
@@ -148,14 +142,12 @@ Unlike `.asResponse()` this method consumes the body, returning once it is parse
```ts
const client = new Parallel();
-const response = await client.taskRun
- .create({ input: 'What was the GDP of France in 2023?', processor: 'base' })
- .asResponse();
+const response = await client.taskRun.create({ input: 'France (2023)', processor: 'processor' }).asResponse();
console.log(response.headers.get('X-My-Header'));
console.log(response.statusText); // access the underlying Response object
const { data: taskRun, response: raw } = await client.taskRun
- .create({ input: 'What was the GDP of France in 2023?', processor: 'base' })
+ .create({ input: 'France (2023)', processor: 'processor' })
.withResponse();
console.log(raw.headers.get('X-My-Header'));
console.log(taskRun.run_id);
diff --git a/api.md b/api.md
index b41a73e..c20960c 100644
--- a/api.md
+++ b/api.md
@@ -4,18 +4,13 @@ Types:
- ErrorObject
- ErrorResponse
-- SourcePolicy
-- Warning
# TaskRun
Types:
-- AutoSchema
- Citation
-- FieldBasis
- JsonSchema
-- RunInput
- TaskRun
- TaskRunJsonOutput
- TaskRunResult
@@ -31,49 +26,6 @@ Methods:
# Beta
-Types:
-
-- SearchResult
-- WebSearchResult
-
-Methods:
-
-- client.beta.search({ ...params }) -> SearchResult
-
## TaskRun
-Types:
-
-- BetaRunInput
-- BetaTaskRunResult
-- ErrorEvent
-- McpServer
-- McpToolCall
-- ParallelBeta
-- TaskRunEvent
-- Webhook
-- TaskRunEventsResponse
-
-Methods:
-
-- client.beta.taskRun.create({ ...params }) -> TaskRun
-- client.beta.taskRun.events(runID) -> TaskRunEventsResponse
-- client.beta.taskRun.result(runID, { ...params }) -> BetaTaskRunResult
-
## TaskGroup
-
-Types:
-
-- TaskGroup
-- TaskGroupRunResponse
-- TaskGroupStatus
-- TaskGroupEventsResponse
-- TaskGroupGetRunsResponse
-
-Methods:
-
-- client.beta.taskGroup.create({ ...params }) -> TaskGroup
-- client.beta.taskGroup.retrieve(taskGroupID) -> TaskGroup
-- client.beta.taskGroup.addRuns(taskGroupID, { ...params }) -> TaskGroupRunResponse
-- client.beta.taskGroup.events(taskGroupID, { ...params }) -> TaskGroupEventsResponse
-- client.beta.taskGroup.getRuns(taskGroupID, { ...params }) -> TaskGroupGetRunsResponse
diff --git a/bin/check-release-environment b/bin/check-release-environment
index a0e7396..6b43775 100644
--- a/bin/check-release-environment
+++ b/bin/check-release-environment
@@ -2,10 +2,6 @@
errors=()
-if [ -z "${STAINLESS_API_KEY}" ]; then
- errors+=("The STAINLESS_API_KEY secret has not been set. Please contact Stainless for an API key & set it in your organization secrets on GitHub.")
-fi
-
lenErrors=${#errors[@]}
if [[ lenErrors -gt 0 ]]; then
diff --git a/scripts/detect-breaking-changes b/scripts/detect-breaking-changes
index 52c6340..69dba46 100755
--- a/scripts/detect-breaking-changes
+++ b/scripts/detect-breaking-changes
@@ -6,13 +6,7 @@ cd "$(dirname "$0")/.."
echo "==> Detecting breaking changes"
-TEST_PATHS=(
- tests/api-resources/task-run.test.ts
- tests/api-resources/beta/beta.test.ts
- tests/api-resources/beta/task-run.test.ts
- tests/api-resources/beta/task-group.test.ts
- tests/index.test.ts
-)
+TEST_PATHS=( tests/api-resources/task-run.test.ts tests/index.test.ts )
for PATHSPEC in "${TEST_PATHS[@]}"; do
# Try to check out previous versions of the test files
diff --git a/src/client.ts b/src/client.ts
index 4aa25c1..044fd17 100644
--- a/src/client.ts
+++ b/src/client.ts
@@ -17,11 +17,8 @@ import * as Uploads from './core/uploads';
import * as API from './resources/index';
import { APIPromise } from './core/api-promise';
import {
- AutoSchema,
Citation,
- FieldBasis,
JsonSchema,
- RunInput,
TaskRun,
TaskRunCreateParams,
TaskRunJsonOutput,
@@ -740,11 +737,8 @@ export declare namespace Parallel {
export {
type TaskRun as TaskRun,
- type AutoSchema as AutoSchema,
type Citation as Citation,
- type FieldBasis as FieldBasis,
type JsonSchema as JsonSchema,
- type RunInput as RunInput,
type TaskRunJsonOutput as TaskRunJsonOutput,
type TaskRunResult as TaskRunResult,
type TaskRunTextOutput as TaskRunTextOutput,
@@ -758,6 +752,4 @@ export declare namespace Parallel {
export type ErrorObject = API.ErrorObject;
export type ErrorResponse = API.ErrorResponse;
- export type SourcePolicy = API.SourcePolicy;
- export type Warning = API.Warning;
}
diff --git a/src/core/streaming.ts b/src/core/streaming.ts
deleted file mode 100644
index 57924d1..0000000
--- a/src/core/streaming.ts
+++ /dev/null
@@ -1,315 +0,0 @@
-import { ParallelError } from './error';
-import { type ReadableStream } from '../internal/shim-types';
-import { makeReadableStream } from '../internal/shims';
-import { findDoubleNewlineIndex, LineDecoder } from '../internal/decoders/line';
-import { ReadableStreamToAsyncIterable } from '../internal/shims';
-import { isAbortError } from '../internal/errors';
-import { encodeUTF8 } from '../internal/utils/bytes';
-import { loggerFor } from '../internal/utils/log';
-import type { Parallel } from '../client';
-
-type Bytes = string | ArrayBuffer | Uint8Array | null | undefined;
-
-export type ServerSentEvent = {
- event: string | null;
- data: string;
- raw: string[];
-};
-
-export class Stream- implements AsyncIterable
- {
- controller: AbortController;
- #client: Parallel | undefined;
-
- constructor(
- private iterator: () => AsyncIterator
- ,
- controller: AbortController,
- client?: Parallel,
- ) {
- this.controller = controller;
- this.#client = client;
- }
-
- static fromSSEResponse
- (
- response: Response,
- controller: AbortController,
- client?: Parallel,
- ): Stream
- {
- let consumed = false;
- const logger = client ? loggerFor(client) : console;
-
- async function* iterator(): AsyncIterator
- {
- if (consumed) {
- throw new ParallelError('Cannot iterate over a consumed stream, use `.tee()` to split the stream.');
- }
- consumed = true;
- let done = false;
- try {
- for await (const sse of _iterSSEMessages(response, controller)) {
- try {
- yield JSON.parse(sse.data);
- } catch (e) {
- logger.error(`Could not parse message into JSON:`, sse.data);
- logger.error(`From chunk:`, sse.raw);
- throw e;
- }
- }
- done = true;
- } catch (e) {
- // If the user calls `stream.controller.abort()`, we should exit without throwing.
- if (isAbortError(e)) return;
- throw e;
- } finally {
- // If the user `break`s, abort the ongoing request.
- if (!done) controller.abort();
- }
- }
-
- return new Stream(iterator, controller, client);
- }
-
- /**
- * Generates a Stream from a newline-separated ReadableStream
- * where each item is a JSON value.
- */
- static fromReadableStream
- (
- readableStream: ReadableStream,
- controller: AbortController,
- client?: Parallel,
- ): Stream
- {
- let consumed = false;
-
- async function* iterLines(): AsyncGenerator {
- const lineDecoder = new LineDecoder();
-
- const iter = ReadableStreamToAsyncIterable(readableStream);
- for await (const chunk of iter) {
- for (const line of lineDecoder.decode(chunk)) {
- yield line;
- }
- }
-
- for (const line of lineDecoder.flush()) {
- yield line;
- }
- }
-
- async function* iterator(): AsyncIterator
- {
- if (consumed) {
- throw new ParallelError('Cannot iterate over a consumed stream, use `.tee()` to split the stream.');
- }
- consumed = true;
- let done = false;
- try {
- for await (const line of iterLines()) {
- if (done) continue;
- if (line) yield JSON.parse(line);
- }
- done = true;
- } catch (e) {
- // If the user calls `stream.controller.abort()`, we should exit without throwing.
- if (isAbortError(e)) return;
- throw e;
- } finally {
- // If the user `break`s, abort the ongoing request.
- if (!done) controller.abort();
- }
- }
-
- return new Stream(iterator, controller, client);
- }
-
- [Symbol.asyncIterator](): AsyncIterator
- {
- return this.iterator();
- }
-
- /**
- * Splits the stream into two streams which can be
- * independently read from at different speeds.
- */
- tee(): [Stream
- , Stream
- ] {
- const left: Array>> = [];
- const right: Array>> = [];
- const iterator = this.iterator();
-
- const teeIterator = (queue: Array>>): AsyncIterator
- => {
- return {
- next: () => {
- if (queue.length === 0) {
- const result = iterator.next();
- left.push(result);
- right.push(result);
- }
- return queue.shift()!;
- },
- };
- };
-
- return [
- new Stream(() => teeIterator(left), this.controller, this.#client),
- new Stream(() => teeIterator(right), this.controller, this.#client),
- ];
- }
-
- /**
- * Converts this stream to a newline-separated ReadableStream of
- * JSON stringified values in the stream
- * which can be turned back into a Stream with `Stream.fromReadableStream()`.
- */
- toReadableStream(): ReadableStream {
- const self = this;
- let iter: AsyncIterator
- ;
-
- return makeReadableStream({
- async start() {
- iter = self[Symbol.asyncIterator]();
- },
- async pull(ctrl: any) {
- try {
- const { value, done } = await iter.next();
- if (done) return ctrl.close();
-
- const bytes = encodeUTF8(JSON.stringify(value) + '\n');
-
- ctrl.enqueue(bytes);
- } catch (err) {
- ctrl.error(err);
- }
- },
- async cancel() {
- await iter.return?.();
- },
- });
- }
-}
-
-export async function* _iterSSEMessages(
- response: Response,
- controller: AbortController,
-): AsyncGenerator {
- if (!response.body) {
- controller.abort();
- if (
- typeof (globalThis as any).navigator !== 'undefined' &&
- (globalThis as any).navigator.product === 'ReactNative'
- ) {
- throw new ParallelError(
- `The default react-native fetch implementation does not support streaming. Please use expo/fetch: https://docs.expo.dev/versions/latest/sdk/expo/#expofetch-api`,
- );
- }
- throw new ParallelError(`Attempted to iterate over a response with no body`);
- }
-
- const sseDecoder = new SSEDecoder();
- const lineDecoder = new LineDecoder();
-
- const iter = ReadableStreamToAsyncIterable(response.body);
- for await (const sseChunk of iterSSEChunks(iter)) {
- for (const line of lineDecoder.decode(sseChunk)) {
- const sse = sseDecoder.decode(line);
- if (sse) yield sse;
- }
- }
-
- for (const line of lineDecoder.flush()) {
- const sse = sseDecoder.decode(line);
- if (sse) yield sse;
- }
-}
-
-/**
- * Given an async iterable iterator, iterates over it and yields full
- * SSE chunks, i.e. yields when a double new-line is encountered.
- */
-async function* iterSSEChunks(iterator: AsyncIterableIterator): AsyncGenerator {
- let data = new Uint8Array();
-
- for await (const chunk of iterator) {
- if (chunk == null) {
- continue;
- }
-
- const binaryChunk =
- chunk instanceof ArrayBuffer ? new Uint8Array(chunk)
- : typeof chunk === 'string' ? encodeUTF8(chunk)
- : chunk;
-
- let newData = new Uint8Array(data.length + binaryChunk.length);
- newData.set(data);
- newData.set(binaryChunk, data.length);
- data = newData;
-
- let patternIndex;
- while ((patternIndex = findDoubleNewlineIndex(data)) !== -1) {
- yield data.slice(0, patternIndex);
- data = data.slice(patternIndex);
- }
- }
-
- if (data.length > 0) {
- yield data;
- }
-}
-
-class SSEDecoder {
- private data: string[];
- private event: string | null;
- private chunks: string[];
-
- constructor() {
- this.event = null;
- this.data = [];
- this.chunks = [];
- }
-
- decode(line: string) {
- if (line.endsWith('\r')) {
- line = line.substring(0, line.length - 1);
- }
-
- if (!line) {
- // empty line and we didn't previously encounter any messages
- if (!this.event && !this.data.length) return null;
-
- const sse: ServerSentEvent = {
- event: this.event,
- data: this.data.join('\n'),
- raw: this.chunks,
- };
-
- this.event = null;
- this.data = [];
- this.chunks = [];
-
- return sse;
- }
-
- this.chunks.push(line);
-
- if (line.startsWith(':')) {
- return null;
- }
-
- let [fieldname, _, value] = partition(line, ':');
-
- if (value.startsWith(' ')) {
- value = value.substring(1);
- }
-
- if (fieldname === 'event') {
- this.event = value;
- } else if (fieldname === 'data') {
- this.data.push(value);
- }
-
- return null;
- }
-}
-
-function partition(str: string, delimiter: string): [string, string, string] {
- const index = str.indexOf(delimiter);
- if (index !== -1) {
- return [str.substring(0, index), delimiter, str.substring(index + delimiter.length)];
- }
-
- return [str, '', ''];
-}
diff --git a/src/internal/decoders/line.ts b/src/internal/decoders/line.ts
deleted file mode 100644
index b3bfa97..0000000
--- a/src/internal/decoders/line.ts
+++ /dev/null
@@ -1,135 +0,0 @@
-import { concatBytes, decodeUTF8, encodeUTF8 } from '../utils/bytes';
-
-export type Bytes = string | ArrayBuffer | Uint8Array | null | undefined;
-
-/**
- * A re-implementation of httpx's `LineDecoder` in Python that handles incrementally
- * reading lines from text.
- *
- * https://github.com/encode/httpx/blob/920333ea98118e9cf617f246905d7b202510941c/httpx/_decoders.py#L258
- */
-export class LineDecoder {
- // prettier-ignore
- static NEWLINE_CHARS = new Set(['\n', '\r']);
- static NEWLINE_REGEXP = /\r\n|[\n\r]/g;
-
- #buffer: Uint8Array;
- #carriageReturnIndex: number | null;
-
- constructor() {
- this.#buffer = new Uint8Array();
- this.#carriageReturnIndex = null;
- }
-
- decode(chunk: Bytes): string[] {
- if (chunk == null) {
- return [];
- }
-
- const binaryChunk =
- chunk instanceof ArrayBuffer ? new Uint8Array(chunk)
- : typeof chunk === 'string' ? encodeUTF8(chunk)
- : chunk;
-
- this.#buffer = concatBytes([this.#buffer, binaryChunk]);
-
- const lines: string[] = [];
- let patternIndex;
- while ((patternIndex = findNewlineIndex(this.#buffer, this.#carriageReturnIndex)) != null) {
- if (patternIndex.carriage && this.#carriageReturnIndex == null) {
- // skip until we either get a corresponding `\n`, a new `\r` or nothing
- this.#carriageReturnIndex = patternIndex.index;
- continue;
- }
-
- // we got double \r or \rtext\n
- if (
- this.#carriageReturnIndex != null &&
- (patternIndex.index !== this.#carriageReturnIndex + 1 || patternIndex.carriage)
- ) {
- lines.push(decodeUTF8(this.#buffer.subarray(0, this.#carriageReturnIndex - 1)));
- this.#buffer = this.#buffer.subarray(this.#carriageReturnIndex);
- this.#carriageReturnIndex = null;
- continue;
- }
-
- const endIndex =
- this.#carriageReturnIndex !== null ? patternIndex.preceding - 1 : patternIndex.preceding;
-
- const line = decodeUTF8(this.#buffer.subarray(0, endIndex));
- lines.push(line);
-
- this.#buffer = this.#buffer.subarray(patternIndex.index);
- this.#carriageReturnIndex = null;
- }
-
- return lines;
- }
-
- flush(): string[] {
- if (!this.#buffer.length) {
- return [];
- }
- return this.decode('\n');
- }
-}
-
-/**
- * This function searches the buffer for the end patterns, (\r or \n)
- * and returns an object with the index preceding the matched newline and the
- * index after the newline char. `null` is returned if no new line is found.
- *
- * ```ts
- * findNewLineIndex('abc\ndef') -> { preceding: 2, index: 3 }
- * ```
- */
-function findNewlineIndex(
- buffer: Uint8Array,
- startIndex: number | null,
-): { preceding: number; index: number; carriage: boolean } | null {
- const newline = 0x0a; // \n
- const carriage = 0x0d; // \r
-
- for (let i = startIndex ?? 0; i < buffer.length; i++) {
- if (buffer[i] === newline) {
- return { preceding: i, index: i + 1, carriage: false };
- }
-
- if (buffer[i] === carriage) {
- return { preceding: i, index: i + 1, carriage: true };
- }
- }
-
- return null;
-}
-
-export function findDoubleNewlineIndex(buffer: Uint8Array): number {
- // This function searches the buffer for the end patterns (\r\r, \n\n, \r\n\r\n)
- // and returns the index right after the first occurrence of any pattern,
- // or -1 if none of the patterns are found.
- const newline = 0x0a; // \n
- const carriage = 0x0d; // \r
-
- for (let i = 0; i < buffer.length - 1; i++) {
- if (buffer[i] === newline && buffer[i + 1] === newline) {
- // \n\n
- return i + 2;
- }
- if (buffer[i] === carriage && buffer[i + 1] === carriage) {
- // \r\r
- return i + 2;
- }
- if (
- buffer[i] === carriage &&
- buffer[i + 1] === newline &&
- i + 3 < buffer.length &&
- buffer[i + 2] === carriage &&
- buffer[i + 3] === newline
- ) {
- // \r\n\r\n
- return i + 4;
- }
- }
-
- return -1;
-}
diff --git a/src/internal/parse.ts b/src/internal/parse.ts
index 914d001..a17b213 100644
--- a/src/internal/parse.ts
+++ b/src/internal/parse.ts
@@ -1,7 +1,6 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
import type { FinalRequestOptions } from './request-options';
-import { Stream } from '../core/streaming';
import { type Parallel } from '../client';
import { formatRequestDetails, loggerFor } from './utils/log';
@@ -17,19 +16,6 @@ export type APIResponseProps = {
export async function defaultParseResponse(client: Parallel, props: APIResponseProps): Promise {
const { response, requestLogID, retryOfRequestLogID, startTime } = props;
const body = await (async () => {
- if (props.options.stream) {
- loggerFor(client).debug('response', response.status, response.url, response.headers, response.body);
-
- // Note: there is an invariant here that isn't represented in the type system
- // that if you set `stream: true` the response type must also be `Stream`
-
- if (props.options.__streamClass) {
- return props.options.__streamClass.fromSSEResponse(response, props.controller, client) as any;
- }
-
- return Stream.fromSSEResponse(response, props.controller, client) as any;
- }
-
// fetch refuses to read the body when the status code is 204.
if (response.status === 204) {
return null as T;
diff --git a/src/internal/request-options.ts b/src/internal/request-options.ts
index 56765e5..2aabf9a 100644
--- a/src/internal/request-options.ts
+++ b/src/internal/request-options.ts
@@ -3,7 +3,6 @@
import { NullableHeaders } from './headers';
import type { BodyInit } from './builtin-types';
-import { Stream } from '../core/streaming';
import type { HTTPMethod, MergedRequestInit } from './types';
import { type HeadersLike } from './headers';
@@ -77,7 +76,6 @@ export type RequestOptions = {
defaultBaseURL?: string | undefined;
__binaryResponse?: boolean | undefined;
- __streamClass?: typeof Stream;
};
export type EncodedContent = { bodyHeaders: HeadersLike; body: BodyInit };
diff --git a/src/resources/beta/beta.ts b/src/resources/beta/beta.ts
index c0de3d7..648f9c3 100644
--- a/src/resources/beta/beta.ts
+++ b/src/resources/beta/beta.ts
@@ -1,157 +1,21 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
import { APIResource } from '../../core/resource';
-import * as Shared from '../shared';
import * as TaskGroupAPI from './task-group';
-import {
- TaskGroup,
- TaskGroupAddRunsParams,
- TaskGroupCreateParams,
- TaskGroupEventsParams,
- TaskGroupEventsResponse,
- TaskGroupGetRunsParams,
- TaskGroupGetRunsResponse,
- TaskGroupRunResponse,
- TaskGroupStatus,
-} from './task-group';
+import { TaskGroup } from './task-group';
import * as TaskRunAPI from './task-run';
-import {
- BetaRunInput,
- BetaTaskRunResult,
- ErrorEvent,
- McpServer,
- McpToolCall,
- ParallelBeta,
- TaskRun,
- TaskRunCreateParams,
- TaskRunEvent,
- TaskRunEventsResponse,
- TaskRunResultParams,
- Webhook,
-} from './task-run';
-import { APIPromise } from '../../core/api-promise';
-import { RequestOptions } from '../../internal/request-options';
+import { TaskRun } from './task-run';
export class Beta extends APIResource {
taskRun: TaskRunAPI.TaskRun = new TaskRunAPI.TaskRun(this._client);
taskGroup: TaskGroupAPI.TaskGroup = new TaskGroupAPI.TaskGroup(this._client);
-
- /**
- * Searches the web.
- */
- search(body: BetaSearchParams, options?: RequestOptions): APIPromise {
- return this._client.post('/v1beta/search', { body, ...options });
- }
-}
-
-/**
- * Output for the Search API.
- */
-export interface SearchResult {
- /**
- * A list of WebSearchResult objects, ordered by decreasing relevance.
- */
- results: Array;
-
- /**
- * Search ID. Example: `search_cad0a6d2-dec0-46bd-95ae-900527d880e7`
- */
- search_id: string;
-}
-
-/**
- * A single search result from the web search API.
- */
-export interface WebSearchResult {
- /**
- * Text excerpts from the search result which are relevant to the request.
- */
- excerpts: Array;
-
- /**
- * Title of the search result.
- */
- title: string;
-
- /**
- * URL associated with the search result.
- */
- url: string;
-}
-
-export interface BetaSearchParams {
- /**
- * Upper bound on the number of characters to include in excerpts for each search
- * result.
- */
- max_chars_per_result?: number | null;
-
- /**
- * Upper bound on the number of results to return. May be limited by the processor.
- * Defaults to 10 if not provided.
- */
- max_results?: number | null;
-
- /**
- * Natural-language description of what the web search is trying to find. May
- * include guidance about preferred sources or freshness. At least one of objective
- * or search_queries must be provided.
- */
- objective?: string | null;
-
- /**
- * Search processor.
- */
- processor?: 'base' | 'pro';
-
- /**
- * Optional list of traditional keyword search queries to guide the search. May
- * contain search operators. At least one of objective or search_queries must be
- * provided.
- */
- search_queries?: Array | null;
-
- /**
- * Source policy for web search results.
- *
- * This policy governs which sources are allowed/disallowed in results.
- */
- source_policy?: Shared.SourcePolicy | null;
}
Beta.TaskRun = TaskRun;
+Beta.TaskGroup = TaskGroup;
export declare namespace Beta {
- export {
- type SearchResult as SearchResult,
- type WebSearchResult as WebSearchResult,
- type BetaSearchParams as BetaSearchParams,
- };
-
- export {
- TaskRun as TaskRun,
- type BetaRunInput as BetaRunInput,
- type BetaTaskRunResult as BetaTaskRunResult,
- type ErrorEvent as ErrorEvent,
- type McpServer as McpServer,
- type McpToolCall as McpToolCall,
- type ParallelBeta as ParallelBeta,
- type TaskRunEvent as TaskRunEvent,
- type Webhook as Webhook,
- type TaskRunEventsResponse as TaskRunEventsResponse,
- type TaskRunCreateParams as TaskRunCreateParams,
- type TaskRunResultParams as TaskRunResultParams,
- };
+ export { TaskRun as TaskRun };
- export {
- type TaskGroup as TaskGroup,
- type TaskGroupRunResponse as TaskGroupRunResponse,
- type TaskGroupStatus as TaskGroupStatus,
- type TaskGroupEventsResponse as TaskGroupEventsResponse,
- type TaskGroupGetRunsResponse as TaskGroupGetRunsResponse,
- type TaskGroupCreateParams as TaskGroupCreateParams,
- type TaskGroupAddRunsParams as TaskGroupAddRunsParams,
- type TaskGroupEventsParams as TaskGroupEventsParams,
- type TaskGroupGetRunsParams as TaskGroupGetRunsParams,
- };
+ export { TaskGroup as TaskGroup };
}
diff --git a/src/resources/beta/index.ts b/src/resources/beta/index.ts
index 061e58c..bf1f5d2 100644
--- a/src/resources/beta/index.ts
+++ b/src/resources/beta/index.ts
@@ -1,28 +1,5 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
export { Beta } from './beta';
-export {
- TaskGroup,
- type TaskGroupRunResponse,
- type TaskGroupStatus,
- type TaskGroupEventsResponse,
- type TaskGroupGetRunsResponse,
- type TaskGroupCreateParams,
- type TaskGroupAddRunsParams,
- type TaskGroupEventsParams,
- type TaskGroupGetRunsParams,
-} from './task-group';
-export {
- TaskRun,
- type BetaRunInput,
- type BetaTaskRunResult,
- type ErrorEvent,
- type McpServer,
- type McpToolCall,
- type ParallelBeta,
- type TaskRunEvent,
- type Webhook,
- type TaskRunEventsResponse,
- type TaskRunCreateParams,
- type TaskRunResultParams,
-} from './task-run';
+export { TaskGroup } from './task-group';
+export { TaskRun } from './task-run';
diff --git a/src/resources/beta/task-group.ts b/src/resources/beta/task-group.ts
index f47f308..bfdaad5 100644
--- a/src/resources/beta/task-group.ts
+++ b/src/resources/beta/task-group.ts
@@ -1,270 +1,5 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
import { APIResource } from '../../core/resource';
-import * as TaskGroupAPI from './task-group';
-import * as TaskRunAPI from '../task-run';
-import * as BetaTaskRunAPI from './task-run';
-import { APIPromise } from '../../core/api-promise';
-import { Stream } from '../../core/streaming';
-import { buildHeaders } from '../../internal/headers';
-import { RequestOptions } from '../../internal/request-options';
-import { path } from '../../internal/utils/path';
-export class TaskGroup extends APIResource {
- /**
- * Initiates a TaskGroup to group and track multiple runs.
- */
- create(body: TaskGroupCreateParams, options?: RequestOptions): APIPromise {
- return this._client.post('/v1beta/tasks/groups', { body, ...options });
- }
-
- /**
- * Retrieves aggregated status across runs in a TaskGroup.
- */
- retrieve(taskGroupID: string, options?: RequestOptions): APIPromise {
- return this._client.get(path`/v1beta/tasks/groups/${taskGroupID}`, options);
- }
-
- /**
- * Initiates multiple task runs within a TaskGroup.
- */
- addRuns(
- taskGroupID: string,
- params: TaskGroupAddRunsParams,
- options?: RequestOptions,
- ): APIPromise {
- const { betas, ...body } = params;
- return this._client.post(path`/v1beta/tasks/groups/${taskGroupID}/runs`, {
- body,
- ...options,
- headers: buildHeaders([
- { ...(betas?.toString() != null ? { 'parallel-beta': betas?.toString() } : undefined) },
- options?.headers,
- ]),
- });
- }
-
- /**
- * Streams events from a TaskGroup: status updates and run completions.
- *
- * The connection will remain open for up to 10 minutes as long as at least one run
- * in the TaskGroup is active.
- */
- events(
- taskGroupID: string,
- query: TaskGroupEventsParams | undefined = {},
- options?: RequestOptions,
- ): APIPromise> {
- return this._client.get(path`/v1beta/tasks/groups/${taskGroupID}/events`, {
- query,
- ...options,
- headers: buildHeaders([{ Accept: 'text/event-stream' }, options?.headers]),
- stream: true,
- }) as APIPromise>;
- }
-
- /**
- * Retrieves task runs in a TaskGroup and optionally their inputs and outputs.
- */
- getRuns(
- taskGroupID: string,
- query: TaskGroupGetRunsParams | undefined = {},
- options?: RequestOptions,
- ): APIPromise> {
- return this._client.get(path`/v1beta/tasks/groups/${taskGroupID}/runs`, {
- query,
- ...options,
- headers: buildHeaders([{ Accept: 'text/event-stream' }, options?.headers]),
- stream: true,
- }) as APIPromise>;
- }
-}
-
-/**
- * Response object for a task group, including its status and metadata.
- */
-export interface TaskGroup {
- /**
- * Timestamp of the creation of the group, as an RFC 3339 string.
- */
- created_at: string | null;
-
- /**
- * Status of a task group.
- */
- status: TaskGroupStatus;
-
- /**
- * ID of the group.
- */
- taskgroup_id: string;
-
- /**
- * User-provided metadata stored with the group.
- */
- metadata?: { [key: string]: string | number | boolean } | null;
-}
-
-/**
- * Response from adding new task runs to a task group.
- */
-export interface TaskGroupRunResponse {
- /**
- * Cursor for these runs in the event stream at
- * taskgroup/events?last_event_id=. Empty for the first runs in the
- * group.
- */
- event_cursor: string | null;
-
- /**
- * Cursor for these runs in the run stream at
- * taskgroup/runs?last_event_id=. Empty for the first runs in the
- * group.
- */
- run_cursor: string | null;
-
- /**
- * IDs of the newly created runs.
- */
- run_ids: Array;
-
- /**
- * Status of a task group.
- */
- status: TaskGroupStatus;
-}
-
-/**
- * Status of a task group.
- */
-export interface TaskGroupStatus {
- /**
- * True if at least one run in the group is currently active, i.e. status is one of
- * {'cancelling', 'queued', 'running'}.
- */
- is_active: boolean;
-
- /**
- * Timestamp of the last status update to the group, as an RFC 3339 string.
- */
- modified_at: string | null;
-
- /**
- * Number of task runs in the group.
- */
- num_task_runs: number;
-
- /**
- * Human-readable status message for the group.
- */
- status_message: string | null;
-
- /**
- * Number of task runs with each status.
- */
- task_run_status_counts: { [key: string]: number };
-}
-
-/**
- * Event indicating an update to group status.
- */
-export type TaskGroupEventsResponse =
- | TaskGroupEventsResponse.TaskGroupStatusEvent
- | BetaTaskRunAPI.TaskRunEvent
- | BetaTaskRunAPI.ErrorEvent;
-
-export namespace TaskGroupEventsResponse {
- /**
- * Event indicating an update to group status.
- */
- export interface TaskGroupStatusEvent {
- /**
- * Cursor to resume the event stream.
- */
- event_id: string;
-
- /**
- * Status of a task group.
- */
- status: TaskGroupAPI.TaskGroupStatus;
-
- /**
- * Event type; always 'task_group_status'.
- */
- type: 'task_group_status';
- }
-}
-
-/**
- * Event when a task run transitions to a non-active status.
- *
- * May indicate completion, cancellation, or failure.
- */
-export type TaskGroupGetRunsResponse = BetaTaskRunAPI.TaskRunEvent | BetaTaskRunAPI.ErrorEvent;
-
-export interface TaskGroupCreateParams {
- /**
- * User-provided metadata stored with the task group.
- */
- metadata?: { [key: string]: string | number | boolean } | null;
-}
-
-export interface TaskGroupAddRunsParams {
- /**
- * Body param: List of task runs to execute.
- */
- inputs: Array;
-
- /**
- * Body param: Specification for a task.
- *
- * Auto output schemas can be specified by setting `output_schema={"type":"auto"}`.
- * Not specifying a TaskSpec is the same as setting an auto output schema.
- *
- * For convenience bare strings are also accepted as input or output schemas.
- */
- default_task_spec?: TaskRunAPI.TaskSpec | null;
-
- /**
- * Header param: Optional header to specify the beta version(s) to enable.
- */
- betas?: Array;
-}
-
-export interface TaskGroupEventsParams {
- last_event_id?: string | null;
-
- timeout?: number | null;
-}
-
-export interface TaskGroupGetRunsParams {
- include_input?: boolean;
-
- include_output?: boolean;
-
- last_event_id?: string | null;
-
- status?:
- | 'queued'
- | 'action_required'
- | 'running'
- | 'completed'
- | 'failed'
- | 'cancelling'
- | 'cancelled'
- | null;
-}
-
-export declare namespace TaskGroup {
- export {
- type TaskGroup as TaskGroup,
- type TaskGroupRunResponse as TaskGroupRunResponse,
- type TaskGroupStatus as TaskGroupStatus,
- type TaskGroupEventsResponse as TaskGroupEventsResponse,
- type TaskGroupGetRunsResponse as TaskGroupGetRunsResponse,
- type TaskGroupCreateParams as TaskGroupCreateParams,
- type TaskGroupAddRunsParams as TaskGroupAddRunsParams,
- type TaskGroupEventsParams as TaskGroupEventsParams,
- type TaskGroupGetRunsParams as TaskGroupGetRunsParams,
- };
-}
+export class TaskGroup extends APIResource {}
diff --git a/src/resources/beta/task-run.ts b/src/resources/beta/task-run.ts
index bfaf20f..e42a175 100644
--- a/src/resources/beta/task-run.ts
+++ b/src/resources/beta/task-run.ts
@@ -1,517 +1,5 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
import { APIResource } from '../../core/resource';
-import * as BetaTaskRunAPI from './task-run';
-import * as Shared from '../shared';
-import * as TaskRunAPI from '../task-run';
-import { APIPromise } from '../../core/api-promise';
-import { Stream } from '../../core/streaming';
-import { buildHeaders } from '../../internal/headers';
-import { RequestOptions } from '../../internal/request-options';
-import { path } from '../../internal/utils/path';
-export class TaskRun extends APIResource {
- /**
- * Initiates a task run.
- *
- * Returns immediately with a run object in status 'queued'.
- *
- * Beta features can be enabled by setting the 'parallel-beta' header.
- */
- create(params: TaskRunCreateParams, options?: RequestOptions): APIPromise {
- const { betas, ...body } = params;
- return this._client.post('/v1/tasks/runs?beta=true', {
- body,
- ...options,
- headers: buildHeaders([
- { ...(betas?.toString() != null ? { 'parallel-beta': betas?.toString() } : undefined) },
- options?.headers,
- ]),
- });
- }
-
- /**
- * Streams events for a task run.
- *
- * Returns a stream of events showing progress updates and state changes for the
- * task run.
- *
- * For task runs that did not have enable_events set to true during creation, the
- * frequency of events will be reduced.
- */
- events(runID: string, options?: RequestOptions): APIPromise> {
- return this._client.get(path`/v1beta/tasks/runs/${runID}/events`, {
- ...options,
- headers: buildHeaders([{ Accept: 'text/event-stream' }, options?.headers]),
- stream: true,
- }) as APIPromise>;
- }
-
- /**
- * Retrieves a run result by run_id, blocking until the run is completed.
- */
- result(
- runID: string,
- params: TaskRunResultParams | null | undefined = {},
- options?: RequestOptions,
- ): APIPromise {
- const { betas, ...query } = params ?? {};
- return this._client.get(path`/v1/tasks/runs/${runID}/result?beta=true`, {
- query,
- ...options,
- headers: buildHeaders([
- { ...(betas?.toString() != null ? { 'parallel-beta': betas?.toString() } : undefined) },
- options?.headers,
- ]),
- });
- }
-}
-
-/**
- * Task run input with additional beta fields.
- */
-export interface BetaRunInput {
- /**
- * Input to the task, either text or a JSON object.
- */
- input: string | { [key: string]: unknown };
-
- /**
- * Processor to use for the task.
- */
- processor: string;
-
- /**
- * Controls tracking of task run execution progress. When set to true, progress
- * events are recorded and can be accessed via the
- * [Task Run events](https://platform.parallel.ai/api-reference) endpoint. When
- * false, no progress events are tracked. Note that progress tracking cannot be
- * enabled after a run has been created. The flag is set to true by default for
- * premium processors (pro and above). This feature is not available via the Python
- * SDK. To enable this feature in your API requests, specify the `parallel-beta`
- * header with `events-sse-2025-07-24` value.
- */
- enable_events?: boolean | null;
-
- /**
- * Optional list of MCP servers to use for the run. This feature is not available
- * via the Python SDK. To enable this feature in your API requests, specify the
- * `parallel-beta` header with `mcp-server-2025-07-17` value.
- */
- mcp_servers?: Array | null;
-
- /**
- * User-provided metadata stored with the run. Keys and values must be strings with
- * a maximum length of 16 and 512 characters respectively.
- */
- metadata?: { [key: string]: string | number | boolean } | null;
-
- /**
- * Source policy for web search results.
- *
- * This policy governs which sources are allowed/disallowed in results.
- */
- source_policy?: Shared.SourcePolicy | null;
-
- /**
- * Specification for a task.
- *
- * Auto output schemas can be specified by setting `output_schema={"type":"auto"}`.
- * Not specifying a TaskSpec is the same as setting an auto output schema.
- *
- * For convenience bare strings are also accepted as input or output schemas.
- */
- task_spec?: TaskRunAPI.TaskSpec | null;
-
- /**
- * Webhooks for Task Runs.
- */
- webhook?: Webhook | null;
-}
-
-/**
- * Result of a beta task run. Available only if beta headers are specified.
- */
-export interface BetaTaskRunResult {
- /**
- * Output from the task conforming to the output schema.
- */
- output: BetaTaskRunResult.BetaTaskRunTextOutput | BetaTaskRunResult.BetaTaskRunJsonOutput;
-
- /**
- * Status of a task run.
- */
- run: TaskRunAPI.TaskRun;
-}
-
-export namespace BetaTaskRunResult {
- /**
- * Output from a task that returns text.
- */
- export interface BetaTaskRunTextOutput {
- /**
- * Basis for the output.
- */
- basis: Array;
-
- /**
- * Text output from the task.
- */
- content: string;
-
- /**
- * The type of output being returned, as determined by the output schema of the
- * task spec.
- */
- type: 'text';
-
- /**
- * Always None.
- */
- beta_fields?: { [key: string]: unknown } | null;
-
- /**
- * MCP tool calls made by the task.
- */
- mcp_tool_calls?: Array | null;
- }
-
- /**
- * Output from a task that returns JSON.
- */
- export interface BetaTaskRunJsonOutput {
- /**
- * Basis for the output.
- */
- basis: Array;
-
- /**
- * Output from the task as a native JSON object, as determined by the output schema
- * of the task spec.
- */
- content: { [key: string]: unknown };
-
- /**
- * The type of output being returned, as determined by the output schema of the
- * task spec.
- */
- type: 'json';
-
- /**
- * Always None.
- */
- beta_fields?: { [key: string]: unknown } | null;
-
- /**
- * MCP tool calls made by the task.
- */
- mcp_tool_calls?: Array | null;
-
- /**
- * Output schema for the Task Run. Populated only if the task was executed with an
- * auto schema.
- */
- output_schema?: { [key: string]: unknown } | null;
- }
-}
-
-/**
- * Event indicating an error.
- */
-export interface ErrorEvent {
- /**
- * An error message.
- */
- error: Shared.ErrorObject;
-
- /**
- * Event type; always 'error'.
- */
- type: 'error';
-}
-
-/**
- * MCP server configuration.
- */
-export interface McpServer {
- /**
- * Name of the MCP server.
- */
- name: string;
-
- /**
- * URL of the MCP server.
- */
- url: string;
-
- /**
- * List of allowed tools for the MCP server.
- */
- allowed_tools?: Array | null;
-
- /**
- * Headers for the MCP server.
- */
- headers?: { [key: string]: string } | null;
-
- /**
- * Type of MCP server being configured. Always `url`.
- */
- type?: 'url';
-}
-
-/**
- * Result of an MCP tool call.
- */
-export interface McpToolCall {
- /**
- * Arguments used to call the MCP tool.
- */
- arguments: string;
-
- /**
- * Name of the MCP server.
- */
- server_name: string;
-
- /**
- * Identifier for the tool call.
- */
- tool_call_id: string;
-
- /**
- * Name of the tool being called.
- */
- tool_name: string;
-
- /**
- * Output received from the tool call, if successful.
- */
- content?: string | null;
-
- /**
- * Error message if the tool call failed.
- */
- error?: string | null;
-}
-
-/**
- * Model for the parallel-beta header.
- */
-export type ParallelBeta =
- | 'mcp-server-2025-07-17'
- | 'events-sse-2025-07-24'
- | 'webhook-2025-08-12'
- | (string & {});
-
-/**
- * Event when a task run transitions to a non-active status.
- *
- * May indicate completion, cancellation, or failure.
- */
-export interface TaskRunEvent {
- /**
- * Cursor to resume the event stream. Always empty for non Task Group runs.
- */
- event_id: string | null;
-
- /**
- * Status of a task run.
- */
- run: TaskRunAPI.TaskRun;
-
- /**
- * Event type; always 'task_run.state'.
- */
- type: 'task_run.state';
-
- /**
- * Task run input with additional beta fields.
- */
- input?: BetaRunInput | null;
-
- /**
- * Output from the run; included only if requested and if status == `completed`.
- */
- output?: TaskRunAPI.TaskRunTextOutput | TaskRunAPI.TaskRunJsonOutput | null;
-}
-
-/**
- * Webhooks for Task Runs.
- */
-export interface Webhook {
- /**
- * URL for the webhook.
- */
- url: string;
-
- /**
- * Event types to send the webhook notifications for.
- */
- event_types?: Array<'task_run.status'>;
-}
-
-/**
- * A progress update for a task run.
- */
-export type TaskRunEventsResponse =
- | TaskRunEventsResponse.TaskRunProgressStatsEvent
- | TaskRunEventsResponse.TaskRunProgressMessageEvent
- | TaskRunEvent
- | ErrorEvent;
-
-export namespace TaskRunEventsResponse {
- /**
- * A progress update for a task run.
- */
- export interface TaskRunProgressStatsEvent {
- /**
- * Source stats for a task run.
- */
- source_stats: TaskRunProgressStatsEvent.SourceStats;
-
- /**
- * Event type; always 'task_run.progress_stats'.
- */
- type: 'task_run.progress_stats';
- }
-
- export namespace TaskRunProgressStatsEvent {
- /**
- * Source stats for a task run.
- */
- export interface SourceStats {
- /**
- * Number of sources considered in processing the task.
- */
- num_sources_considered: number | null;
-
- /**
- * Number of sources read in processing the task.
- */
- num_sources_read: number | null;
-
- /**
- * A sample of URLs of sources read in processing the task.
- */
- sources_read_sample: Array | null;
- }
- }
-
- /**
- * A message for a task run progress update.
- */
- export interface TaskRunProgressMessageEvent {
- /**
- * Progress update message.
- */
- message: string;
-
- /**
- * Timestamp of the message.
- */
- timestamp: string | null;
-
- /**
- * Event type; always starts with 'task_run.progress_msg'.
- */
- type:
- | 'task_run.progress_msg.plan'
- | 'task_run.progress_msg.search'
- | 'task_run.progress_msg.result'
- | 'task_run.progress_msg.tool_call'
- | 'task_run.progress_msg.exec_status';
- }
-}
-
-export interface TaskRunCreateParams {
- /**
- * Body param: Input to the task, either text or a JSON object.
- */
- input: string | { [key: string]: unknown };
-
- /**
- * Body param: Processor to use for the task.
- */
- processor: string;
-
- /**
- * Body param: Controls tracking of task run execution progress. When set to true,
- * progress events are recorded and can be accessed via the
- * [Task Run events](https://platform.parallel.ai/api-reference) endpoint. When
- * false, no progress events are tracked. Note that progress tracking cannot be
- * enabled after a run has been created. The flag is set to true by default for
- * premium processors (pro and above). This feature is not available via the Python
- * SDK. To enable this feature in your API requests, specify the `parallel-beta`
- * header with `events-sse-2025-07-24` value.
- */
- enable_events?: boolean | null;
-
- /**
- * Body param: Optional list of MCP servers to use for the run. This feature is not
- * available via the Python SDK. To enable this feature in your API requests,
- * specify the `parallel-beta` header with `mcp-server-2025-07-17` value.
- */
- mcp_servers?: Array | null;
-
- /**
- * Body param: User-provided metadata stored with the run. Keys and values must be
- * strings with a maximum length of 16 and 512 characters respectively.
- */
- metadata?: { [key: string]: string | number | boolean } | null;
-
- /**
- * Body param: Source policy for web search results.
- *
- * This policy governs which sources are allowed/disallowed in results.
- */
- source_policy?: Shared.SourcePolicy | null;
-
- /**
- * Body param: Specification for a task.
- *
- * Auto output schemas can be specified by setting `output_schema={"type":"auto"}`.
- * Not specifying a TaskSpec is the same as setting an auto output schema.
- *
- * For convenience bare strings are also accepted as input or output schemas.
- */
- task_spec?: TaskRunAPI.TaskSpec | null;
-
- /**
- * Body param: Webhooks for Task Runs.
- */
- webhook?: Webhook | null;
-
- /**
- * Header param: Optional header to specify the beta version(s) to enable.
- */
- betas?: Array;
-}
-
-export interface TaskRunResultParams {
- /**
- * Query param:
- */
- timeout?: number;
-
- /**
- * Header param: Optional header to specify the beta version(s) to enable.
- */
- betas?: Array;
-}
-
-export declare namespace TaskRun {
- export {
- type BetaRunInput as BetaRunInput,
- type BetaTaskRunResult as BetaTaskRunResult,
- type ErrorEvent as ErrorEvent,
- type McpServer as McpServer,
- type McpToolCall as McpToolCall,
- type ParallelBeta as ParallelBeta,
- type TaskRunEvent as TaskRunEvent,
- type Webhook as Webhook,
- type TaskRunEventsResponse as TaskRunEventsResponse,
- type TaskRunCreateParams as TaskRunCreateParams,
- type TaskRunResultParams as TaskRunResultParams,
- };
-}
+export class TaskRun extends APIResource {}
diff --git a/src/resources/index.ts b/src/resources/index.ts
index 709471e..911d9ca 100644
--- a/src/resources/index.ts
+++ b/src/resources/index.ts
@@ -4,11 +4,8 @@ export * from './shared';
export { Beta } from './beta/beta';
export {
TaskRun,
- type AutoSchema,
type Citation,
- type FieldBasis,
type JsonSchema,
- type RunInput,
type TaskRunJsonOutput,
type TaskRunResult,
type TaskRunTextOutput,
diff --git a/src/resources/shared.ts b/src/resources/shared.ts
index 5bb1c1a..0ec2535 100644
--- a/src/resources/shared.ts
+++ b/src/resources/shared.ts
@@ -17,7 +17,7 @@ export interface ErrorObject {
/**
* Optional detail supporting the error.
*/
- detail?: { [key: string]: unknown } | null;
+ detail?: unknown | null;
}
/**
@@ -32,45 +32,5 @@ export interface ErrorResponse {
/**
* Always 'error'.
*/
- type: 'error';
-}
-
-/**
- * Source policy for web search results.
- *
- * This policy governs which sources are allowed/disallowed in results.
- */
-export interface SourcePolicy {
- /**
- * List of domains to exclude from results. If specified, sources from these
- * domains will be excluded.
- */
- exclude_domains?: Array;
-
- /**
- * List of domains to restrict the results to. If specified, only sources from
- * these domains will be included.
- */
- include_domains?: Array;
-}
-
-/**
- * Human-readable message for a task.
- */
-export interface Warning {
- /**
- * Human-readable message.
- */
- message: string;
-
- /**
- * Type of warning. Note that adding new warning types is considered a
- * backward-compatible change.
- */
- type: 'spec_validation_warning' | 'input_validation_warning' | 'warning';
-
- /**
- * Optional detail supporting the warning.
- */
- detail?: { [key: string]: unknown } | null;
+ type?: 'error';
}
diff --git a/src/resources/task-run.ts b/src/resources/task-run.ts
index 6cada92..bac8e38 100644
--- a/src/resources/task-run.ts
+++ b/src/resources/task-run.ts
@@ -1,34 +1,28 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
import { APIResource } from '../core/resource';
-import * as Shared from './shared';
+import * as TaskRunAPI from './task-run';
import { APIPromise } from '../core/api-promise';
import { RequestOptions } from '../internal/request-options';
import { path } from '../internal/utils/path';
export class TaskRun extends APIResource {
/**
- * Initiates a task run.
- *
- * Returns immediately with a run object in status 'queued'.
- *
- * Beta features can be enabled by setting the 'parallel-beta' header.
+ * Initiates a single task run.
*/
create(body: TaskRunCreateParams, options?: RequestOptions): APIPromise {
return this._client.post('/v1/tasks/runs', { body, ...options });
}
/**
- * Retrieves run status by run_id.
- *
- * The run result is available from the `/result` endpoint.
+ * Retrieves a run by run_id.
*/
retrieve(runID: string, options?: RequestOptions): APIPromise {
return this._client.get(path`/v1/tasks/runs/${runID}`, options);
}
/**
- * Retrieves a run result by run_id, blocking until the run is completed.
+ * Retrieves a run by run_id, blocking until the run is completed.
*/
result(
runID: string,
@@ -39,16 +33,6 @@ export class TaskRun extends APIResource {
}
}
-/**
- * Auto schema for a task input or output.
- */
-export interface AutoSchema {
- /**
- * The type of schema being defined. Always `auto`.
- */
- type?: 'auto';
-}
-
/**
* A citation for a task output.
*/
@@ -70,32 +54,6 @@ export interface Citation {
title?: string | null;
}
-/**
- * Citations and reasoning supporting one field of a task output.
- */
-export interface FieldBasis {
- /**
- * Name of the output field.
- */
- field: string;
-
- /**
- * Reasoning for the output field.
- */
- reasoning: string;
-
- /**
- * List of citations supporting the output field.
- */
- citations?: Array;
-
- /**
- * Confidence level for the output field. Only certain processors provide
- * confidence levels.
- */
- confidence?: string | null;
-}
-
/**
* JSON schema for a task input or output.
*/
@@ -103,7 +61,7 @@ export interface JsonSchema {
/**
* A JSON Schema object. Only a subset of JSON Schema is supported.
*/
- json_schema: { [key: string]: unknown };
+ json_schema: unknown;
/**
* The type of schema being defined. Always `json`.
@@ -112,45 +70,7 @@ export interface JsonSchema {
}
/**
- * Request to run a task.
- */
-export interface RunInput {
- /**
- * Input to the task, either text or a JSON object.
- */
- input: string | { [key: string]: unknown };
-
- /**
- * Processor to use for the task.
- */
- processor: string;
-
- /**
- * User-provided metadata stored with the run. Keys and values must be strings with
- * a maximum length of 16 and 512 characters respectively.
- */
- metadata?: { [key: string]: string | number | boolean } | null;
-
- /**
- * Source policy for web search results.
- *
- * This policy governs which sources are allowed/disallowed in results.
- */
- source_policy?: Shared.SourcePolicy | null;
-
- /**
- * Specification for a task.
- *
- * Auto output schemas can be specified by setting `output_schema={"type":"auto"}`.
- * Not specifying a TaskSpec is the same as setting an auto output schema.
- *
- * For convenience bare strings are also accepted as input or output schemas.
- */
- task_spec?: TaskSpec | null;
-}
-
-/**
- * Status of a task run.
+ * Status of a task.
*/
export interface TaskRun {
/**
@@ -159,8 +79,8 @@ export interface TaskRun {
created_at: string | null;
/**
- * Whether the run is currently active, i.e. status is one of {'cancelling',
- * 'queued', 'running'}.
+ * Whether the run is currently active; i.e. status is one of {'running', 'queued',
+ * 'cancelling'}.
*/
is_active: boolean;
@@ -184,67 +104,88 @@ export interface TaskRun {
*/
status: 'queued' | 'action_required' | 'running' | 'completed' | 'failed' | 'cancelling' | 'cancelled';
- /**
- * An error message.
- */
- error?: Shared.ErrorObject | null;
-
/**
* User-provided metadata stored with the run.
*/
metadata?: { [key: string]: string | number | boolean } | null;
/**
- * ID of the taskgroup to which the run belongs.
+ * Warnings for the run.
*/
- taskgroup_id?: string | null;
+ warnings?: Array | null;
+}
+export namespace TaskRun {
/**
- * Warnings for the run, if any.
+ * Human-readable message for a task.
*/
- warnings?: Array | null;
+ export interface Warning {
+ /**
+ * Human-readable message.
+ */
+ message: string;
+
+ /**
+ * Type of warning. Note that adding new warning types is considered a
+ * backward-compatible change.
+ */
+ type: string;
+
+ /**
+ * Optional detail supporting the warning.
+ */
+ detail?: unknown | null;
+ }
}
/**
- * Output from a task that returns JSON.
+ * Output from a task that returns text.
*/
export interface TaskRunJsonOutput {
/**
* Basis for each top-level field in the JSON output.
*/
- basis: Array;
+ basis: Array;
/**
* Output from the task as a native JSON object, as determined by the output schema
* of the task spec.
*/
- content: { [key: string]: unknown };
+ content: unknown;
/**
* The type of output being returned, as determined by the output schema of the
* task spec.
*/
type: 'json';
+}
+export namespace TaskRunJsonOutput {
/**
- * Additional fields from beta features used in this task run. When beta features
- * are specified during both task run creation and result retrieval, this field
- * will be empty and instead the relevant beta attributes will be directly included
- * in the `BetaTaskRunJsonOutput` or corresponding output type. However, if beta
- * features were specified during task run creation but not during result
- * retrieval, this field will contain the dump of fields from those beta features.
- * Each key represents the beta feature version (one amongst parallel-beta headers)
- * and the values correspond to the beta feature attributes, if any. For now, only
- * MCP server beta features have attributes. For example,
- * `{mcp-server-2025-07-17: [{'server_name':'mcp_server', 'tool_call_id': 'tc_123', ...}]}}`
+ * Citations and reasoning supporting one field of a task output.
*/
- beta_fields?: { [key: string]: unknown } | null;
+ export interface Basis {
+ /**
+ * Name of the output field.
+ */
+ field: string;
- /**
- * Output schema for the Task Run. Populated only if the task was executed with an
- * auto schema.
- */
- output_schema?: { [key: string]: unknown } | null;
+ /**
+ * Reasoning for the output field.
+ */
+ reasoning: string;
+
+ /**
+ * List of citations supporting the output field.
+ */
+ citations?: Array;
+
+ /**
+ * Confidence level for the output field. Only certain processors provide
+ * confidence levels.
+ */
+ confidence?: string | null;
+ }
}
/**
@@ -257,7 +198,7 @@ export interface TaskRunResult {
output: TaskRunTextOutput | TaskRunJsonOutput;
/**
- * Status of a task run.
+ * Status of a task.
*/
run: TaskRun;
}
@@ -269,7 +210,7 @@ export interface TaskRunTextOutput {
/**
* Basis for the output. The basis has a single field 'output'.
*/
- basis: Array;
+ basis: Array;
/**
* Text output from the task.
@@ -281,29 +222,41 @@ export interface TaskRunTextOutput {
* task spec.
*/
type: 'text';
+}
+export namespace TaskRunTextOutput {
/**
- * Additional fields from beta features used in this task run. When beta features
- * are specified during both task run creation and result retrieval, this field
- * will be empty and instead the relevant beta attributes will be directly included
- * in the `BetaTaskRunJsonOutput` or corresponding output type. However, if beta
- * features were specified during task run creation but not during result
- * retrieval, this field will contain the dump of fields from those beta features.
- * Each key represents the beta feature version (one amongst parallel-beta headers)
- * and the values correspond to the beta feature attributes, if any. For now, only
- * MCP server beta features have attributes. For example,
- * `{mcp-server-2025-07-17: [{'server_name':'mcp_server', 'tool_call_id': 'tc_123', ...}]}}`
+ * Citations and reasoning supporting one field of a task output.
*/
- beta_fields?: { [key: string]: unknown } | null;
+ export interface Basis {
+ /**
+ * Name of the output field.
+ */
+ field: string;
+
+ /**
+ * Reasoning for the output field.
+ */
+ reasoning: string;
+
+ /**
+ * List of citations supporting the output field.
+ */
+ citations?: Array;
+
+ /**
+ * Confidence level for the output field. Only certain processors provide
+ * confidence levels.
+ */
+ confidence?: string | null;
+ }
}
/**
* Specification for a task.
*
- * Auto output schemas can be specified by setting `output_schema={"type":"auto"}`.
- * Not specifying a TaskSpec is the same as setting an auto output schema.
- *
- * For convenience bare strings are also accepted as input or output schemas.
+ * For convenience we allow bare strings as input or output schemas, which is
+ * equivalent to a text schema with the same description.
*/
export interface TaskSpec {
/**
@@ -312,13 +265,13 @@ export interface TaskSpec {
* response. A bare string is equivalent to a text schema with the same
* description.
*/
- output_schema: JsonSchema | TextSchema | AutoSchema | string;
+ output_schema: JsonSchema | TextSchema | string;
/**
* Optional JSON schema or text description of expected input to the task. A bare
* string is equivalent to a text schema with the same description.
*/
- input_schema?: string | JsonSchema | TextSchema | null;
+ input_schema?: JsonSchema | TextSchema | string | null;
}
/**
@@ -340,7 +293,7 @@ export interface TaskRunCreateParams {
/**
* Input to the task, either text or a JSON object.
*/
- input: string | { [key: string]: unknown };
+ input: string | unknown;
/**
* Processor to use for the task.
@@ -353,20 +306,11 @@ export interface TaskRunCreateParams {
*/
metadata?: { [key: string]: string | number | boolean } | null;
- /**
- * Source policy for web search results.
- *
- * This policy governs which sources are allowed/disallowed in results.
- */
- source_policy?: Shared.SourcePolicy | null;
-
/**
* Specification for a task.
*
- * Auto output schemas can be specified by setting `output_schema={"type":"auto"}`.
- * Not specifying a TaskSpec is the same as setting an auto output schema.
- *
- * For convenience bare strings are also accepted as input or output schemas.
+ * For convenience we allow bare strings as input or output schemas, which is
+ * equivalent to a text schema with the same description.
*/
task_spec?: TaskSpec | null;
}
@@ -377,11 +321,8 @@ export interface TaskRunResultParams {
export declare namespace TaskRun {
export {
- type AutoSchema as AutoSchema,
type Citation as Citation,
- type FieldBasis as FieldBasis,
type JsonSchema as JsonSchema,
- type RunInput as RunInput,
type TaskRun as TaskRun,
type TaskRunJsonOutput as TaskRunJsonOutput,
type TaskRunResult as TaskRunResult,
diff --git a/src/streaming.ts b/src/streaming.ts
deleted file mode 100644
index 9e6da10..0000000
--- a/src/streaming.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-/** @deprecated Import from ./core/streaming instead */
-export * from './core/streaming';
diff --git a/tests/api-resources/beta/beta.test.ts b/tests/api-resources/beta/beta.test.ts
deleted file mode 100644
index f6a03e3..0000000
--- a/tests/api-resources/beta/beta.test.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-import Parallel from 'parallel-web';
-
-const client = new Parallel({
- apiKey: 'My API Key',
- baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
-});
-
-describe('resource beta', () => {
- test('search', async () => {
- const responsePromise = client.beta.search({});
- const rawResponse = await responsePromise.asResponse();
- expect(rawResponse).toBeInstanceOf(Response);
- const response = await responsePromise;
- expect(response).not.toBeInstanceOf(Response);
- const dataAndResponse = await responsePromise.withResponse();
- expect(dataAndResponse.data).toBe(response);
- expect(dataAndResponse.response).toBe(rawResponse);
- });
-});
diff --git a/tests/api-resources/beta/task-group.test.ts b/tests/api-resources/beta/task-group.test.ts
deleted file mode 100644
index 612b8bc..0000000
--- a/tests/api-resources/beta/task-group.test.ts
+++ /dev/null
@@ -1,126 +0,0 @@
-// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-import Parallel from 'parallel-web';
-
-const client = new Parallel({
- apiKey: 'My API Key',
- baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
-});
-
-describe('resource taskGroup', () => {
- test('create', async () => {
- const responsePromise = client.beta.taskGroup.create({});
- const rawResponse = await responsePromise.asResponse();
- expect(rawResponse).toBeInstanceOf(Response);
- const response = await responsePromise;
- expect(response).not.toBeInstanceOf(Response);
- const dataAndResponse = await responsePromise.withResponse();
- expect(dataAndResponse.data).toBe(response);
- expect(dataAndResponse.response).toBe(rawResponse);
- });
-
- test('retrieve', async () => {
- const responsePromise = client.beta.taskGroup.retrieve('taskgroup_id');
- const rawResponse = await responsePromise.asResponse();
- expect(rawResponse).toBeInstanceOf(Response);
- const response = await responsePromise;
- expect(response).not.toBeInstanceOf(Response);
- const dataAndResponse = await responsePromise.withResponse();
- expect(dataAndResponse.data).toBe(response);
- expect(dataAndResponse.response).toBe(rawResponse);
- });
-
- test('addRuns: only required params', async () => {
- const responsePromise = client.beta.taskGroup.addRuns('taskgroup_id', {
- inputs: [{ input: 'What was the GDP of France in 2023?', processor: 'base' }],
- });
- const rawResponse = await responsePromise.asResponse();
- expect(rawResponse).toBeInstanceOf(Response);
- const response = await responsePromise;
- expect(response).not.toBeInstanceOf(Response);
- const dataAndResponse = await responsePromise.withResponse();
- expect(dataAndResponse.data).toBe(response);
- expect(dataAndResponse.response).toBe(rawResponse);
- });
-
- test('addRuns: required and optional params', async () => {
- const response = await client.beta.taskGroup.addRuns('taskgroup_id', {
- inputs: [
- {
- input: 'What was the GDP of France in 2023?',
- processor: 'base',
- enable_events: true,
- mcp_servers: [
- { name: 'name', url: 'url', allowed_tools: ['string'], headers: { foo: 'string' }, type: 'url' },
- ],
- metadata: { foo: 'string' },
- source_policy: { exclude_domains: ['string'], include_domains: ['string'] },
- task_spec: {
- output_schema: {
- json_schema: { additionalProperties: 'bar', properties: 'bar', required: 'bar', type: 'bar' },
- type: 'json',
- },
- input_schema: 'string',
- },
- webhook: { url: 'url', event_types: ['task_run.status'] },
- },
- ],
- default_task_spec: {
- output_schema: {
- json_schema: { additionalProperties: 'bar', properties: 'bar', required: 'bar', type: 'bar' },
- type: 'json',
- },
- input_schema: 'string',
- },
- betas: ['mcp-server-2025-07-17'],
- });
- });
-
- // Prism doesn't support text/event-stream responses
- test.skip('events', async () => {
- const responsePromise = client.beta.taskGroup.events('taskgroup_id');
- const rawResponse = await responsePromise.asResponse();
- expect(rawResponse).toBeInstanceOf(Response);
- const response = await responsePromise;
- expect(response).not.toBeInstanceOf(Response);
- const dataAndResponse = await responsePromise.withResponse();
- expect(dataAndResponse.data).toBe(response);
- expect(dataAndResponse.response).toBe(rawResponse);
- });
-
- // Prism doesn't support text/event-stream responses
- test.skip('events: request options and params are passed correctly', async () => {
- // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
- await expect(
- client.beta.taskGroup.events(
- 'taskgroup_id',
- { last_event_id: 'last_event_id', timeout: 0 },
- { path: '/_stainless_unknown_path' },
- ),
- ).rejects.toThrow(Parallel.NotFoundError);
- });
-
- // Prism doesn't support text/event-stream responses
- test.skip('getRuns', async () => {
- const responsePromise = client.beta.taskGroup.getRuns('taskgroup_id');
- const rawResponse = await responsePromise.asResponse();
- expect(rawResponse).toBeInstanceOf(Response);
- const response = await responsePromise;
- expect(response).not.toBeInstanceOf(Response);
- const dataAndResponse = await responsePromise.withResponse();
- expect(dataAndResponse.data).toBe(response);
- expect(dataAndResponse.response).toBe(rawResponse);
- });
-
- // Prism doesn't support text/event-stream responses
- test.skip('getRuns: request options and params are passed correctly', async () => {
- // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
- await expect(
- client.beta.taskGroup.getRuns(
- 'taskgroup_id',
- { include_input: true, include_output: true, last_event_id: 'last_event_id', status: 'queued' },
- { path: '/_stainless_unknown_path' },
- ),
- ).rejects.toThrow(Parallel.NotFoundError);
- });
-});
diff --git a/tests/api-resources/beta/task-run.test.ts b/tests/api-resources/beta/task-run.test.ts
deleted file mode 100644
index 29adaa3..0000000
--- a/tests/api-resources/beta/task-run.test.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-import Parallel from 'parallel-web';
-
-const client = new Parallel({
- apiKey: 'My API Key',
- baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
-});
-
-describe('resource taskRun', () => {
- test('create: only required params', async () => {
- const responsePromise = client.beta.taskRun.create({
- input: 'What was the GDP of France in 2023?',
- processor: 'base',
- });
- const rawResponse = await responsePromise.asResponse();
- expect(rawResponse).toBeInstanceOf(Response);
- const response = await responsePromise;
- expect(response).not.toBeInstanceOf(Response);
- const dataAndResponse = await responsePromise.withResponse();
- expect(dataAndResponse.data).toBe(response);
- expect(dataAndResponse.response).toBe(rawResponse);
- });
-
- test('create: required and optional params', async () => {
- const response = await client.beta.taskRun.create({
- input: 'What was the GDP of France in 2023?',
- processor: 'base',
- enable_events: true,
- mcp_servers: [
- { name: 'name', url: 'url', allowed_tools: ['string'], headers: { foo: 'string' }, type: 'url' },
- ],
- metadata: { foo: 'string' },
- source_policy: { exclude_domains: ['string'], include_domains: ['string'] },
- task_spec: {
- output_schema: {
- json_schema: { additionalProperties: 'bar', properties: 'bar', required: 'bar', type: 'bar' },
- type: 'json',
- },
- input_schema: 'string',
- },
- webhook: { url: 'url', event_types: ['task_run.status'] },
- betas: ['mcp-server-2025-07-17'],
- });
- });
-
- // Prism doesn't support text/event-stream responses
- test.skip('events', async () => {
- const responsePromise = client.beta.taskRun.events('run_id');
- const rawResponse = await responsePromise.asResponse();
- expect(rawResponse).toBeInstanceOf(Response);
- const response = await responsePromise;
- expect(response).not.toBeInstanceOf(Response);
- const dataAndResponse = await responsePromise.withResponse();
- expect(dataAndResponse.data).toBe(response);
- expect(dataAndResponse.response).toBe(rawResponse);
- });
-
- test('result', async () => {
- const responsePromise = client.beta.taskRun.result('run_id');
- const rawResponse = await responsePromise.asResponse();
- expect(rawResponse).toBeInstanceOf(Response);
- const response = await responsePromise;
- expect(response).not.toBeInstanceOf(Response);
- const dataAndResponse = await responsePromise.withResponse();
- expect(dataAndResponse.data).toBe(response);
- expect(dataAndResponse.response).toBe(rawResponse);
- });
-
- test('result: request options and params are passed correctly', async () => {
- // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
- await expect(
- client.beta.taskRun.result(
- 'run_id',
- { timeout: 0, betas: ['mcp-server-2025-07-17'] },
- { path: '/_stainless_unknown_path' },
- ),
- ).rejects.toThrow(Parallel.NotFoundError);
- });
-});
diff --git a/tests/api-resources/task-run.test.ts b/tests/api-resources/task-run.test.ts
index eedd2bb..be6e12b 100644
--- a/tests/api-resources/task-run.test.ts
+++ b/tests/api-resources/task-run.test.ts
@@ -9,10 +9,7 @@ const client = new Parallel({
describe('resource taskRun', () => {
test('create: only required params', async () => {
- const responsePromise = client.taskRun.create({
- input: 'What was the GDP of France in 2023?',
- processor: 'base',
- });
+ const responsePromise = client.taskRun.create({ input: 'France (2023)', processor: 'processor' });
const rawResponse = await responsePromise.asResponse();
expect(rawResponse).toBeInstanceOf(Response);
const response = await responsePromise;
@@ -24,16 +21,38 @@ describe('resource taskRun', () => {
test('create: required and optional params', async () => {
const response = await client.taskRun.create({
- input: 'What was the GDP of France in 2023?',
- processor: 'base',
+ input: 'France (2023)',
+ processor: 'processor',
metadata: { foo: 'string' },
- source_policy: { exclude_domains: ['string'], include_domains: ['string'] },
task_spec: {
output_schema: {
- json_schema: { additionalProperties: 'bar', properties: 'bar', required: 'bar', type: 'bar' },
+ json_schema: {
+ additionalProperties: false,
+ properties: {
+ gdp: {
+ description: "GDP in USD for the year, formatted like '$3.1 trillion (2023)'",
+ type: 'string',
+ },
+ },
+ required: ['gdp'],
+ type: 'object',
+ },
+ type: 'json',
+ },
+ input_schema: {
+ json_schema: {
+ additionalProperties: false,
+ properties: {
+ gdp: {
+ description: "GDP in USD for the year, formatted like '$3.1 trillion (2023)'",
+ type: 'string',
+ },
+ },
+ required: ['gdp'],
+ type: 'object',
+ },
type: 'json',
},
- input_schema: 'string',
},
});
});
diff --git a/tests/internal/decoders/line.test.ts b/tests/internal/decoders/line.test.ts
deleted file mode 100644
index 13e60b9..0000000
--- a/tests/internal/decoders/line.test.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-import { findDoubleNewlineIndex, LineDecoder } from 'parallel-web/internal/decoders/line';
-
-function decodeChunks(chunks: string[], { flush }: { flush: boolean } = { flush: false }): string[] {
- const decoder = new LineDecoder();
- const lines: string[] = [];
- for (const chunk of chunks) {
- lines.push(...decoder.decode(chunk));
- }
-
- if (flush) {
- lines.push(...decoder.flush());
- }
-
- return lines;
-}
-
-describe('line decoder', () => {
- test('basic', () => {
- // baz is not included because the line hasn't ended yet
- expect(decodeChunks(['foo', ' bar\nbaz'])).toEqual(['foo bar']);
- });
-
- test('basic with \\r', () => {
- expect(decodeChunks(['foo', ' bar\r\nbaz'])).toEqual(['foo bar']);
- expect(decodeChunks(['foo', ' bar\r\nbaz'], { flush: true })).toEqual(['foo bar', 'baz']);
- });
-
- test('trailing new lines', () => {
- expect(decodeChunks(['foo', ' bar', 'baz\n', 'thing\n'])).toEqual(['foo barbaz', 'thing']);
- });
-
- test('trailing new lines with \\r', () => {
- expect(decodeChunks(['foo', ' bar', 'baz\r\n', 'thing\r\n'])).toEqual(['foo barbaz', 'thing']);
- });
-
- test('escaped new lines', () => {
- expect(decodeChunks(['foo', ' bar\\nbaz\n'])).toEqual(['foo bar\\nbaz']);
- });
-
- test('escaped new lines with \\r', () => {
- expect(decodeChunks(['foo', ' bar\\r\\nbaz\n'])).toEqual(['foo bar\\r\\nbaz']);
- });
-
- test('\\r & \\n split across multiple chunks', () => {
- expect(decodeChunks(['foo\r', '\n', 'bar'], { flush: true })).toEqual(['foo', 'bar']);
- });
-
- test('single \\r', () => {
- expect(decodeChunks(['foo\r', 'bar'], { flush: true })).toEqual(['foo', 'bar']);
- });
-
- test('double \\r', () => {
- expect(decodeChunks(['foo\r', 'bar\r'], { flush: true })).toEqual(['foo', 'bar']);
- expect(decodeChunks(['foo\r', '\r', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']);
- // implementation detail that we don't yield the single \r line until a new \r or \n is encountered
- expect(decodeChunks(['foo\r', '\r', 'bar'], { flush: false })).toEqual(['foo']);
- });
-
- test('double \\r then \\r\\n', () => {
- expect(decodeChunks(['foo\r', '\r', '\r', '\n', 'bar', '\n'])).toEqual(['foo', '', '', 'bar']);
- expect(decodeChunks(['foo\n', '\n', '\n', 'bar', '\n'])).toEqual(['foo', '', '', 'bar']);
- });
-
- test('double newline', () => {
- expect(decodeChunks(['foo\n\nbar'], { flush: true })).toEqual(['foo', '', 'bar']);
- expect(decodeChunks(['foo', '\n', '\nbar'], { flush: true })).toEqual(['foo', '', 'bar']);
- expect(decodeChunks(['foo\n', '\n', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']);
- expect(decodeChunks(['foo', '\n', '\n', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']);
- });
-
- test('multi-byte characters across chunks', () => {
- const decoder = new LineDecoder();
-
- // bytes taken from the string 'известни' and arbitrarily split
- // so that some multi-byte characters span multiple chunks
- expect(decoder.decode(new Uint8Array([0xd0]))).toHaveLength(0);
- expect(decoder.decode(new Uint8Array([0xb8, 0xd0, 0xb7, 0xd0]))).toHaveLength(0);
- expect(
- decoder.decode(new Uint8Array([0xb2, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82, 0xd0, 0xbd, 0xd0, 0xb8])),
- ).toHaveLength(0);
-
- const decoded = decoder.decode(new Uint8Array([0xa]));
- expect(decoded).toEqual(['известни']);
- });
-
- test('flushing trailing newlines', () => {
- expect(decodeChunks(['foo\n', '\nbar'], { flush: true })).toEqual(['foo', '', 'bar']);
- });
-
- test('flushing empty buffer', () => {
- expect(decodeChunks([], { flush: true })).toEqual([]);
- });
-});
-
-describe('findDoubleNewlineIndex', () => {
- test('finds \\n\\n', () => {
- expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\n\nbar'))).toBe(5);
- expect(findDoubleNewlineIndex(new TextEncoder().encode('\n\nbar'))).toBe(2);
- expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\n\n'))).toBe(5);
- expect(findDoubleNewlineIndex(new TextEncoder().encode('\n\n'))).toBe(2);
- });
-
- test('finds \\r\\r', () => {
- expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\rbar'))).toBe(5);
- expect(findDoubleNewlineIndex(new TextEncoder().encode('\r\rbar'))).toBe(2);
- expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\r'))).toBe(5);
- expect(findDoubleNewlineIndex(new TextEncoder().encode('\r\r'))).toBe(2);
- });
-
- test('finds \\r\\n\\r\\n', () => {
- expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\n\r\nbar'))).toBe(7);
- expect(findDoubleNewlineIndex(new TextEncoder().encode('\r\n\r\nbar'))).toBe(4);
- expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\n\r\n'))).toBe(7);
- expect(findDoubleNewlineIndex(new TextEncoder().encode('\r\n\r\n'))).toBe(4);
- });
-
- test('returns -1 when no double newline found', () => {
- expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\nbar'))).toBe(-1);
- expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\rbar'))).toBe(-1);
- expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\nbar'))).toBe(-1);
- expect(findDoubleNewlineIndex(new TextEncoder().encode(''))).toBe(-1);
- });
-
- test('handles incomplete patterns', () => {
- expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\n\r'))).toBe(-1);
- expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\n'))).toBe(-1);
- });
-});
diff --git a/tests/streaming.test.ts b/tests/streaming.test.ts
deleted file mode 100644
index 068d3af..0000000
--- a/tests/streaming.test.ts
+++ /dev/null
@@ -1,219 +0,0 @@
-import assert from 'assert';
-import { _iterSSEMessages } from 'parallel-web/core/streaming';
-import { ReadableStreamFrom } from 'parallel-web/internal/shims';
-
-describe('streaming decoding', () => {
- test('basic', async () => {
- async function* body(): AsyncGenerator {
- yield Buffer.from('event: completion\n');
- yield Buffer.from('data: {"foo":true}\n');
- yield Buffer.from('\n');
- }
-
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
-
- let event = await stream.next();
- assert(event.value);
- expect(JSON.parse(event.value.data)).toEqual({ foo: true });
-
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
-
- test('data without event', async () => {
- async function* body(): AsyncGenerator {
- yield Buffer.from('data: {"foo":true}\n');
- yield Buffer.from('\n');
- }
-
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
-
- let event = await stream.next();
- assert(event.value);
- expect(event.value.event).toBeNull();
- expect(JSON.parse(event.value.data)).toEqual({ foo: true });
-
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
-
- test('event without data', async () => {
- async function* body(): AsyncGenerator {
- yield Buffer.from('event: foo\n');
- yield Buffer.from('\n');
- }
-
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
-
- let event = await stream.next();
- assert(event.value);
- expect(event.value.event).toEqual('foo');
- expect(event.value.data).toEqual('');
-
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
-
- test('multiple events', async () => {
- async function* body(): AsyncGenerator {
- yield Buffer.from('event: foo\n');
- yield Buffer.from('\n');
- yield Buffer.from('event: ping\n');
- yield Buffer.from('\n');
- }
-
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
-
- let event = await stream.next();
- assert(event.value);
- expect(event.value.event).toEqual('foo');
- expect(event.value.data).toEqual('');
-
- event = await stream.next();
- assert(event.value);
- expect(event.value.event).toEqual('ping');
- expect(event.value.data).toEqual('');
-
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
-
- test('multiple events with data', async () => {
- async function* body(): AsyncGenerator {
- yield Buffer.from('event: foo\n');
- yield Buffer.from('data: {"foo":true}\n');
- yield Buffer.from('\n');
- yield Buffer.from('event: ping\n');
- yield Buffer.from('data: {"bar":false}\n');
- yield Buffer.from('\n');
- }
-
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
-
- let event = await stream.next();
- assert(event.value);
- expect(event.value.event).toEqual('foo');
- expect(JSON.parse(event.value.data)).toEqual({ foo: true });
-
- event = await stream.next();
- assert(event.value);
- expect(event.value.event).toEqual('ping');
- expect(JSON.parse(event.value.data)).toEqual({ bar: false });
-
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
-
- test('multiple data lines with empty line', async () => {
- async function* body(): AsyncGenerator {
- yield Buffer.from('event: ping\n');
- yield Buffer.from('data: {\n');
- yield Buffer.from('data: "foo":\n');
- yield Buffer.from('data: \n');
- yield Buffer.from('data:\n');
- yield Buffer.from('data: true}\n');
- yield Buffer.from('\n\n');
- }
-
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
-
- let event = await stream.next();
- assert(event.value);
- expect(event.value.event).toEqual('ping');
- expect(JSON.parse(event.value.data)).toEqual({ foo: true });
- expect(event.value.data).toEqual('{\n"foo":\n\n\ntrue}');
-
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
-
- test('data json escaped double new line', async () => {
- async function* body(): AsyncGenerator {
- yield Buffer.from('event: ping\n');
- yield Buffer.from('data: {"foo": "my long\\n\\ncontent"}');
- yield Buffer.from('\n\n');
- }
-
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
-
- let event = await stream.next();
- assert(event.value);
- expect(event.value.event).toEqual('ping');
- expect(JSON.parse(event.value.data)).toEqual({ foo: 'my long\n\ncontent' });
-
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
-
- test('special new line characters', async () => {
- async function* body(): AsyncGenerator {
- yield Buffer.from('data: {"content": "culpa "}\n');
- yield Buffer.from('\n');
- yield Buffer.from('data: {"content": "');
- yield Buffer.from([0xe2, 0x80, 0xa8]);
- yield Buffer.from('"}\n');
- yield Buffer.from('\n');
- yield Buffer.from('data: {"content": "foo"}\n');
- yield Buffer.from('\n');
- }
-
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
-
- let event = await stream.next();
- assert(event.value);
- expect(JSON.parse(event.value.data)).toEqual({ content: 'culpa ' });
-
- event = await stream.next();
- assert(event.value);
- expect(JSON.parse(event.value.data)).toEqual({ content: Buffer.from([0xe2, 0x80, 0xa8]).toString() });
-
- event = await stream.next();
- assert(event.value);
- expect(JSON.parse(event.value.data)).toEqual({ content: 'foo' });
-
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
-
- test('multi-byte characters across chunks', async () => {
- async function* body(): AsyncGenerator {
- yield Buffer.from('event: completion\n');
- yield Buffer.from('data: {"content": "');
- // bytes taken from the string 'известни' and arbitrarily split
- // so that some multi-byte characters span multiple chunks
- yield Buffer.from([0xd0]);
- yield Buffer.from([0xb8, 0xd0, 0xb7, 0xd0]);
- yield Buffer.from([0xb2, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82, 0xd0, 0xbd, 0xd0, 0xb8]);
- yield Buffer.from('"}\n');
- yield Buffer.from('\n');
- }
-
- const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
- Symbol.asyncIterator
- ]();
-
- let event = await stream.next();
- assert(event.value);
- expect(event.value.event).toEqual('completion');
- expect(JSON.parse(event.value.data)).toEqual({ content: 'известни' });
-
- event = await stream.next();
- expect(event.done).toBeTruthy();
- });
-});
From 7a985fbf420eed89c87940cef40efb650b554c9b Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Sun, 31 Aug 2025 21:59:16 +0000
Subject: [PATCH 2/4] feat(api): update via SDK Studio
---
.stats.yml | 8 +-
README.md | 22 +-
api.md | 48 ++
scripts/detect-breaking-changes | 8 +-
src/client.ts | 8 +
src/core/streaming.ts | 315 ++++++++++++
src/internal/decoders/line.ts | 135 +++++
src/internal/parse.ts | 14 +
src/internal/request-options.ts | 2 +
src/resources/beta/beta.ts | 146 +++++-
src/resources/beta/index.ts | 27 +-
src/resources/beta/task-group.ts | 267 +++++++++-
src/resources/beta/task-run.ts | 514 +++++++++++++++++++-
src/resources/index.ts | 3 +
src/resources/shared.ts | 44 +-
src/resources/task-run.ts | 241 +++++----
src/streaming.ts | 2 +
tests/api-resources/beta/beta.test.ts | 21 +
tests/api-resources/beta/task-group.test.ts | 126 +++++
tests/api-resources/beta/task-run.test.ts | 80 +++
tests/api-resources/task-run.test.ts | 37 +-
tests/internal/decoders/line.test.ts | 128 +++++
tests/streaming.test.ts | 219 +++++++++
23 files changed, 2273 insertions(+), 142 deletions(-)
create mode 100644 src/core/streaming.ts
create mode 100644 src/internal/decoders/line.ts
create mode 100644 src/streaming.ts
create mode 100644 tests/api-resources/beta/beta.test.ts
create mode 100644 tests/api-resources/beta/task-group.test.ts
create mode 100644 tests/api-resources/beta/task-run.test.ts
create mode 100644 tests/internal/decoders/line.test.ts
create mode 100644 tests/streaming.test.ts
diff --git a/.stats.yml b/.stats.yml
index c703e97..7c4f552 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 3
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/parallel-web%2Fparallel-sdk-ff0d5939e135b67b3448abf72d8bb0f9a574194337c7c7192453781347a9601d.yml
-openapi_spec_hash: f3ce85349af6273a671d3d2781c4c877
-config_hash: 284b51e02bda8519b1f21bb67f1809e0
+configured_endpoints: 12
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/parallel-web%2Fparallel-sdk-1aeb1c81a84999f2d27ca9e86b041d74b892926bed126dc9b0f3cff4d7b26963.yml
+openapi_spec_hash: 6280f6c6fb537f7c9ac5cc33ee2e433d
+config_hash: 451edf5a87ae14248aa336ffc08d216f
diff --git a/README.md b/README.md
index 6dc066d..20d1935 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,10 @@ const client = new Parallel({
apiKey: process.env['PARALLEL_API_KEY'], // This is the default and can be omitted
});
-const taskRun = await client.taskRun.create({ input: 'France (2023)', processor: 'processor' });
+const taskRun = await client.taskRun.create({
+ input: 'What was the GDP of France in 2023?',
+ processor: 'base',
+});
console.log(taskRun.run_id);
```
@@ -46,7 +49,10 @@ const client = new Parallel({
apiKey: process.env['PARALLEL_API_KEY'], // This is the default and can be omitted
});
-const params: Parallel.TaskRunCreateParams = { input: 'France (2023)', processor: 'processor' };
+const params: Parallel.TaskRunCreateParams = {
+ input: 'What was the GDP of France in 2023?',
+ processor: 'base',
+};
const taskRun: Parallel.TaskRun = await client.taskRun.create(params);
```
@@ -61,7 +67,7 @@ a subclass of `APIError` will be thrown:
```ts
const taskRun = await client.taskRun
- .create({ input: 'France (2023)', processor: 'processor' })
+ .create({ input: 'What was the GDP of France in 2023?', processor: 'base' })
.catch(async (err) => {
if (err instanceof Parallel.APIError) {
console.log(err.status); // 400
@@ -102,7 +108,7 @@ const client = new Parallel({
});
// Or, configure per-request:
-await client.taskRun.create({ input: 'France (2023)', processor: 'processor' }, {
+await client.taskRun.create({ input: 'What was the GDP of France in 2023?', processor: 'base' }, {
maxRetries: 5,
});
```
@@ -119,7 +125,7 @@ const client = new Parallel({
});
// Override per-request:
-await client.taskRun.create({ input: 'France (2023)', processor: 'processor' }, {
+await client.taskRun.create({ input: 'What was the GDP of France in 2023?', processor: 'base' }, {
timeout: 5 * 1000,
});
```
@@ -142,12 +148,14 @@ Unlike `.asResponse()` this method consumes the body, returning once it is parse
```ts
const client = new Parallel();
-const response = await client.taskRun.create({ input: 'France (2023)', processor: 'processor' }).asResponse();
+const response = await client.taskRun
+ .create({ input: 'What was the GDP of France in 2023?', processor: 'base' })
+ .asResponse();
console.log(response.headers.get('X-My-Header'));
console.log(response.statusText); // access the underlying Response object
const { data: taskRun, response: raw } = await client.taskRun
- .create({ input: 'France (2023)', processor: 'processor' })
+ .create({ input: 'What was the GDP of France in 2023?', processor: 'base' })
.withResponse();
console.log(raw.headers.get('X-My-Header'));
console.log(taskRun.run_id);
diff --git a/api.md b/api.md
index c20960c..b41a73e 100644
--- a/api.md
+++ b/api.md
@@ -4,13 +4,18 @@ Types:
-
ErrorObject
- ErrorResponse
+- SourcePolicy
+- Warning
# TaskRun
Types:
+- AutoSchema
- Citation
+- FieldBasis
- JsonSchema
+- RunInput
- TaskRun
- TaskRunJsonOutput
- TaskRunResult
@@ -26,6 +31,49 @@ Methods:
# Beta
+Types:
+
+- SearchResult
+- WebSearchResult
+
+Methods:
+
+- client.beta.search({ ...params }) -> SearchResult
+
## TaskRun
+Types:
+
+- BetaRunInput
+- BetaTaskRunResult
+- ErrorEvent
+- McpServer
+- McpToolCall
+- ParallelBeta
+- TaskRunEvent
+- Webhook
+- TaskRunEventsResponse
+
+Methods:
+
+- client.beta.taskRun.create({ ...params }) -> TaskRun
+- client.beta.taskRun.events(runID) -> TaskRunEventsResponse
+- client.beta.taskRun.result(runID, { ...params }) -> BetaTaskRunResult
+
## TaskGroup
+
+Types:
+
+- TaskGroup
+- TaskGroupRunResponse
+- TaskGroupStatus
+- TaskGroupEventsResponse
+- TaskGroupGetRunsResponse
+
+Methods:
+
+- client.beta.taskGroup.create({ ...params }) -> TaskGroup
+- client.beta.taskGroup.retrieve(taskGroupID) -> TaskGroup
+- client.beta.taskGroup.addRuns(taskGroupID, { ...params }) -> TaskGroupRunResponse
+- client.beta.taskGroup.events(taskGroupID, { ...params }) -> TaskGroupEventsResponse
+- client.beta.taskGroup.getRuns(taskGroupID, { ...params }) -> TaskGroupGetRunsResponse
diff --git a/scripts/detect-breaking-changes b/scripts/detect-breaking-changes
index 69dba46..52c6340 100755
--- a/scripts/detect-breaking-changes
+++ b/scripts/detect-breaking-changes
@@ -6,7 +6,13 @@ cd "$(dirname "$0")/.."
echo "==> Detecting breaking changes"
-TEST_PATHS=( tests/api-resources/task-run.test.ts tests/index.test.ts )
+TEST_PATHS=(
+ tests/api-resources/task-run.test.ts
+ tests/api-resources/beta/beta.test.ts
+ tests/api-resources/beta/task-run.test.ts
+ tests/api-resources/beta/task-group.test.ts
+ tests/index.test.ts
+)
for PATHSPEC in "${TEST_PATHS[@]}"; do
# Try to check out previous versions of the test files
diff --git a/src/client.ts b/src/client.ts
index 044fd17..4aa25c1 100644
--- a/src/client.ts
+++ b/src/client.ts
@@ -17,8 +17,11 @@ import * as Uploads from './core/uploads';
import * as API from './resources/index';
import { APIPromise } from './core/api-promise';
import {
+ AutoSchema,
Citation,
+ FieldBasis,
JsonSchema,
+ RunInput,
TaskRun,
TaskRunCreateParams,
TaskRunJsonOutput,
@@ -737,8 +740,11 @@ export declare namespace Parallel {
export {
type TaskRun as TaskRun,
+ type AutoSchema as AutoSchema,
type Citation as Citation,
+ type FieldBasis as FieldBasis,
type JsonSchema as JsonSchema,
+ type RunInput as RunInput,
type TaskRunJsonOutput as TaskRunJsonOutput,
type TaskRunResult as TaskRunResult,
type TaskRunTextOutput as TaskRunTextOutput,
@@ -752,4 +758,6 @@ export declare namespace Parallel {
export type ErrorObject = API.ErrorObject;
export type ErrorResponse = API.ErrorResponse;
+ export type SourcePolicy = API.SourcePolicy;
+ export type Warning = API.Warning;
}
diff --git a/src/core/streaming.ts b/src/core/streaming.ts
new file mode 100644
index 0000000..57924d1
--- /dev/null
+++ b/src/core/streaming.ts
@@ -0,0 +1,315 @@
+import { ParallelError } from './error';
+import { type ReadableStream } from '../internal/shim-types';
+import { makeReadableStream } from '../internal/shims';
+import { findDoubleNewlineIndex, LineDecoder } from '../internal/decoders/line';
+import { ReadableStreamToAsyncIterable } from '../internal/shims';
+import { isAbortError } from '../internal/errors';
+import { encodeUTF8 } from '../internal/utils/bytes';
+import { loggerFor } from '../internal/utils/log';
+import type { Parallel } from '../client';
+
+type Bytes = string | ArrayBuffer | Uint8Array | null | undefined;
+
+export type ServerSentEvent = {
+ event: string | null;
+ data: string;
+ raw: string[];
+};
+
+export class Stream- implements AsyncIterable
- {
+ controller: AbortController;
+ #client: Parallel | undefined;
+
+ constructor(
+ private iterator: () => AsyncIterator
- ,
+ controller: AbortController,
+ client?: Parallel,
+ ) {
+ this.controller = controller;
+ this.#client = client;
+ }
+
+ static fromSSEResponse
- (
+ response: Response,
+ controller: AbortController,
+ client?: Parallel,
+ ): Stream
- {
+ let consumed = false;
+ const logger = client ? loggerFor(client) : console;
+
+ async function* iterator(): AsyncIterator
- {
+ if (consumed) {
+ throw new ParallelError('Cannot iterate over a consumed stream, use `.tee()` to split the stream.');
+ }
+ consumed = true;
+ let done = false;
+ try {
+ for await (const sse of _iterSSEMessages(response, controller)) {
+ try {
+ yield JSON.parse(sse.data);
+ } catch (e) {
+ logger.error(`Could not parse message into JSON:`, sse.data);
+ logger.error(`From chunk:`, sse.raw);
+ throw e;
+ }
+ }
+ done = true;
+ } catch (e) {
+ // If the user calls `stream.controller.abort()`, we should exit without throwing.
+ if (isAbortError(e)) return;
+ throw e;
+ } finally {
+ // If the user `break`s, abort the ongoing request.
+ if (!done) controller.abort();
+ }
+ }
+
+ return new Stream(iterator, controller, client);
+ }
+
+ /**
+ * Generates a Stream from a newline-separated ReadableStream
+ * where each item is a JSON value.
+ */
+ static fromReadableStream
- (
+ readableStream: ReadableStream,
+ controller: AbortController,
+ client?: Parallel,
+ ): Stream
- {
+ let consumed = false;
+
+ async function* iterLines(): AsyncGenerator {
+ const lineDecoder = new LineDecoder();
+
+ const iter = ReadableStreamToAsyncIterable(readableStream);
+ for await (const chunk of iter) {
+ for (const line of lineDecoder.decode(chunk)) {
+ yield line;
+ }
+ }
+
+ for (const line of lineDecoder.flush()) {
+ yield line;
+ }
+ }
+
+ async function* iterator(): AsyncIterator
- {
+ if (consumed) {
+ throw new ParallelError('Cannot iterate over a consumed stream, use `.tee()` to split the stream.');
+ }
+ consumed = true;
+ let done = false;
+ try {
+ for await (const line of iterLines()) {
+ if (done) continue;
+ if (line) yield JSON.parse(line);
+ }
+ done = true;
+ } catch (e) {
+ // If the user calls `stream.controller.abort()`, we should exit without throwing.
+ if (isAbortError(e)) return;
+ throw e;
+ } finally {
+ // If the user `break`s, abort the ongoing request.
+ if (!done) controller.abort();
+ }
+ }
+
+ return new Stream(iterator, controller, client);
+ }
+
+ [Symbol.asyncIterator](): AsyncIterator
- {
+ return this.iterator();
+ }
+
+ /**
+ * Splits the stream into two streams which can be
+ * independently read from at different speeds.
+ */
+ tee(): [Stream
- , Stream
- ] {
+ const left: Array>> = [];
+ const right: Array>> = [];
+ const iterator = this.iterator();
+
+ const teeIterator = (queue: Array>>): AsyncIterator
- => {
+ return {
+ next: () => {
+ if (queue.length === 0) {
+ const result = iterator.next();
+ left.push(result);
+ right.push(result);
+ }
+ return queue.shift()!;
+ },
+ };
+ };
+
+ return [
+ new Stream(() => teeIterator(left), this.controller, this.#client),
+ new Stream(() => teeIterator(right), this.controller, this.#client),
+ ];
+ }
+
+ /**
+ * Converts this stream to a newline-separated ReadableStream of
+ * JSON stringified values in the stream
+ * which can be turned back into a Stream with `Stream.fromReadableStream()`.
+ */
+ toReadableStream(): ReadableStream {
+ const self = this;
+ let iter: AsyncIterator
- ;
+
+ return makeReadableStream({
+ async start() {
+ iter = self[Symbol.asyncIterator]();
+ },
+ async pull(ctrl: any) {
+ try {
+ const { value, done } = await iter.next();
+ if (done) return ctrl.close();
+
+ const bytes = encodeUTF8(JSON.stringify(value) + '\n');
+
+ ctrl.enqueue(bytes);
+ } catch (err) {
+ ctrl.error(err);
+ }
+ },
+ async cancel() {
+ await iter.return?.();
+ },
+ });
+ }
+}
+
+export async function* _iterSSEMessages(
+ response: Response,
+ controller: AbortController,
+): AsyncGenerator {
+ if (!response.body) {
+ controller.abort();
+ if (
+ typeof (globalThis as any).navigator !== 'undefined' &&
+ (globalThis as any).navigator.product === 'ReactNative'
+ ) {
+ throw new ParallelError(
+ `The default react-native fetch implementation does not support streaming. Please use expo/fetch: https://docs.expo.dev/versions/latest/sdk/expo/#expofetch-api`,
+ );
+ }
+ throw new ParallelError(`Attempted to iterate over a response with no body`);
+ }
+
+ const sseDecoder = new SSEDecoder();
+ const lineDecoder = new LineDecoder();
+
+ const iter = ReadableStreamToAsyncIterable(response.body);
+ for await (const sseChunk of iterSSEChunks(iter)) {
+ for (const line of lineDecoder.decode(sseChunk)) {
+ const sse = sseDecoder.decode(line);
+ if (sse) yield sse;
+ }
+ }
+
+ for (const line of lineDecoder.flush()) {
+ const sse = sseDecoder.decode(line);
+ if (sse) yield sse;
+ }
+}
+
+/**
+ * Given an async iterable iterator, iterates over it and yields full
+ * SSE chunks, i.e. yields when a double new-line is encountered.
+ */
+async function* iterSSEChunks(iterator: AsyncIterableIterator): AsyncGenerator {
+ let data = new Uint8Array();
+
+ for await (const chunk of iterator) {
+ if (chunk == null) {
+ continue;
+ }
+
+ const binaryChunk =
+ chunk instanceof ArrayBuffer ? new Uint8Array(chunk)
+ : typeof chunk === 'string' ? encodeUTF8(chunk)
+ : chunk;
+
+ let newData = new Uint8Array(data.length + binaryChunk.length);
+ newData.set(data);
+ newData.set(binaryChunk, data.length);
+ data = newData;
+
+ let patternIndex;
+ while ((patternIndex = findDoubleNewlineIndex(data)) !== -1) {
+ yield data.slice(0, patternIndex);
+ data = data.slice(patternIndex);
+ }
+ }
+
+ if (data.length > 0) {
+ yield data;
+ }
+}
+
+class SSEDecoder {
+ private data: string[];
+ private event: string | null;
+ private chunks: string[];
+
+ constructor() {
+ this.event = null;
+ this.data = [];
+ this.chunks = [];
+ }
+
+ decode(line: string) {
+ if (line.endsWith('\r')) {
+ line = line.substring(0, line.length - 1);
+ }
+
+ if (!line) {
+ // empty line and we didn't previously encounter any messages
+ if (!this.event && !this.data.length) return null;
+
+ const sse: ServerSentEvent = {
+ event: this.event,
+ data: this.data.join('\n'),
+ raw: this.chunks,
+ };
+
+ this.event = null;
+ this.data = [];
+ this.chunks = [];
+
+ return sse;
+ }
+
+ this.chunks.push(line);
+
+ if (line.startsWith(':')) {
+ return null;
+ }
+
+ let [fieldname, _, value] = partition(line, ':');
+
+ if (value.startsWith(' ')) {
+ value = value.substring(1);
+ }
+
+ if (fieldname === 'event') {
+ this.event = value;
+ } else if (fieldname === 'data') {
+ this.data.push(value);
+ }
+
+ return null;
+ }
+}
+
+function partition(str: string, delimiter: string): [string, string, string] {
+ const index = str.indexOf(delimiter);
+ if (index !== -1) {
+ return [str.substring(0, index), delimiter, str.substring(index + delimiter.length)];
+ }
+
+ return [str, '', ''];
+}
diff --git a/src/internal/decoders/line.ts b/src/internal/decoders/line.ts
new file mode 100644
index 0000000..b3bfa97
--- /dev/null
+++ b/src/internal/decoders/line.ts
@@ -0,0 +1,135 @@
+import { concatBytes, decodeUTF8, encodeUTF8 } from '../utils/bytes';
+
+export type Bytes = string | ArrayBuffer | Uint8Array | null | undefined;
+
+/**
+ * A re-implementation of httpx's `LineDecoder` in Python that handles incrementally
+ * reading lines from text.
+ *
+ * https://github.com/encode/httpx/blob/920333ea98118e9cf617f246905d7b202510941c/httpx/_decoders.py#L258
+ */
+export class LineDecoder {
+ // prettier-ignore
+ static NEWLINE_CHARS = new Set(['\n', '\r']);
+ static NEWLINE_REGEXP = /\r\n|[\n\r]/g;
+
+ #buffer: Uint8Array;
+ #carriageReturnIndex: number | null;
+
+ constructor() {
+ this.#buffer = new Uint8Array();
+ this.#carriageReturnIndex = null;
+ }
+
+ decode(chunk: Bytes): string[] {
+ if (chunk == null) {
+ return [];
+ }
+
+ const binaryChunk =
+ chunk instanceof ArrayBuffer ? new Uint8Array(chunk)
+ : typeof chunk === 'string' ? encodeUTF8(chunk)
+ : chunk;
+
+ this.#buffer = concatBytes([this.#buffer, binaryChunk]);
+
+ const lines: string[] = [];
+ let patternIndex;
+ while ((patternIndex = findNewlineIndex(this.#buffer, this.#carriageReturnIndex)) != null) {
+ if (patternIndex.carriage && this.#carriageReturnIndex == null) {
+ // skip until we either get a corresponding `\n`, a new `\r` or nothing
+ this.#carriageReturnIndex = patternIndex.index;
+ continue;
+ }
+
+ // we got double \r or \rtext\n
+ if (
+ this.#carriageReturnIndex != null &&
+ (patternIndex.index !== this.#carriageReturnIndex + 1 || patternIndex.carriage)
+ ) {
+ lines.push(decodeUTF8(this.#buffer.subarray(0, this.#carriageReturnIndex - 1)));
+ this.#buffer = this.#buffer.subarray(this.#carriageReturnIndex);
+ this.#carriageReturnIndex = null;
+ continue;
+ }
+
+ const endIndex =
+ this.#carriageReturnIndex !== null ? patternIndex.preceding - 1 : patternIndex.preceding;
+
+ const line = decodeUTF8(this.#buffer.subarray(0, endIndex));
+ lines.push(line);
+
+ this.#buffer = this.#buffer.subarray(patternIndex.index);
+ this.#carriageReturnIndex = null;
+ }
+
+ return lines;
+ }
+
+ flush(): string[] {
+ if (!this.#buffer.length) {
+ return [];
+ }
+ return this.decode('\n');
+ }
+}
+
+/**
+ * This function searches the buffer for the end patterns, (\r or \n)
+ * and returns an object with the index preceding the matched newline and the
+ * index after the newline char. `null` is returned if no new line is found.
+ *
+ * ```ts
+ * findNewLineIndex('abc\ndef') -> { preceding: 2, index: 3 }
+ * ```
+ */
+function findNewlineIndex(
+ buffer: Uint8Array,
+ startIndex: number | null,
+): { preceding: number; index: number; carriage: boolean } | null {
+ const newline = 0x0a; // \n
+ const carriage = 0x0d; // \r
+
+ for (let i = startIndex ?? 0; i < buffer.length; i++) {
+ if (buffer[i] === newline) {
+ return { preceding: i, index: i + 1, carriage: false };
+ }
+
+ if (buffer[i] === carriage) {
+ return { preceding: i, index: i + 1, carriage: true };
+ }
+ }
+
+ return null;
+}
+
+export function findDoubleNewlineIndex(buffer: Uint8Array): number {
+ // This function searches the buffer for the end patterns (\r\r, \n\n, \r\n\r\n)
+ // and returns the index right after the first occurrence of any pattern,
+ // or -1 if none of the patterns are found.
+ const newline = 0x0a; // \n
+ const carriage = 0x0d; // \r
+
+ for (let i = 0; i < buffer.length - 1; i++) {
+ if (buffer[i] === newline && buffer[i + 1] === newline) {
+ // \n\n
+ return i + 2;
+ }
+ if (buffer[i] === carriage && buffer[i + 1] === carriage) {
+ // \r\r
+ return i + 2;
+ }
+ if (
+ buffer[i] === carriage &&
+ buffer[i + 1] === newline &&
+ i + 3 < buffer.length &&
+ buffer[i + 2] === carriage &&
+ buffer[i + 3] === newline
+ ) {
+ // \r\n\r\n
+ return i + 4;
+ }
+ }
+
+ return -1;
+}
diff --git a/src/internal/parse.ts b/src/internal/parse.ts
index a17b213..914d001 100644
--- a/src/internal/parse.ts
+++ b/src/internal/parse.ts
@@ -1,6 +1,7 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
import type { FinalRequestOptions } from './request-options';
+import { Stream } from '../core/streaming';
import { type Parallel } from '../client';
import { formatRequestDetails, loggerFor } from './utils/log';
@@ -16,6 +17,19 @@ export type APIResponseProps = {
export async function defaultParseResponse(client: Parallel, props: APIResponseProps): Promise {
const { response, requestLogID, retryOfRequestLogID, startTime } = props;
const body = await (async () => {
+ if (props.options.stream) {
+ loggerFor(client).debug('response', response.status, response.url, response.headers, response.body);
+
+ // Note: there is an invariant here that isn't represented in the type system
+ // that if you set `stream: true` the response type must also be `Stream`
+
+ if (props.options.__streamClass) {
+ return props.options.__streamClass.fromSSEResponse(response, props.controller, client) as any;
+ }
+
+ return Stream.fromSSEResponse(response, props.controller, client) as any;
+ }
+
// fetch refuses to read the body when the status code is 204.
if (response.status === 204) {
return null as T;
diff --git a/src/internal/request-options.ts b/src/internal/request-options.ts
index 2aabf9a..56765e5 100644
--- a/src/internal/request-options.ts
+++ b/src/internal/request-options.ts
@@ -3,6 +3,7 @@
import { NullableHeaders } from './headers';
import type { BodyInit } from './builtin-types';
+import { Stream } from '../core/streaming';
import type { HTTPMethod, MergedRequestInit } from './types';
import { type HeadersLike } from './headers';
@@ -76,6 +77,7 @@ export type RequestOptions = {
defaultBaseURL?: string | undefined;
__binaryResponse?: boolean | undefined;
+ __streamClass?: typeof Stream;
};
export type EncodedContent = { bodyHeaders: HeadersLike; body: BodyInit };
diff --git a/src/resources/beta/beta.ts b/src/resources/beta/beta.ts
index 648f9c3..c0de3d7 100644
--- a/src/resources/beta/beta.ts
+++ b/src/resources/beta/beta.ts
@@ -1,21 +1,157 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
import { APIResource } from '../../core/resource';
+import * as Shared from '../shared';
import * as TaskGroupAPI from './task-group';
-import { TaskGroup } from './task-group';
+import {
+ TaskGroup,
+ TaskGroupAddRunsParams,
+ TaskGroupCreateParams,
+ TaskGroupEventsParams,
+ TaskGroupEventsResponse,
+ TaskGroupGetRunsParams,
+ TaskGroupGetRunsResponse,
+ TaskGroupRunResponse,
+ TaskGroupStatus,
+} from './task-group';
import * as TaskRunAPI from './task-run';
-import { TaskRun } from './task-run';
+import {
+ BetaRunInput,
+ BetaTaskRunResult,
+ ErrorEvent,
+ McpServer,
+ McpToolCall,
+ ParallelBeta,
+ TaskRun,
+ TaskRunCreateParams,
+ TaskRunEvent,
+ TaskRunEventsResponse,
+ TaskRunResultParams,
+ Webhook,
+} from './task-run';
+import { APIPromise } from '../../core/api-promise';
+import { RequestOptions } from '../../internal/request-options';
export class Beta extends APIResource {
taskRun: TaskRunAPI.TaskRun = new TaskRunAPI.TaskRun(this._client);
taskGroup: TaskGroupAPI.TaskGroup = new TaskGroupAPI.TaskGroup(this._client);
+
+ /**
+ * Searches the web.
+ */
+ search(body: BetaSearchParams, options?: RequestOptions): APIPromise {
+ return this._client.post('/v1beta/search', { body, ...options });
+ }
+}
+
+/**
+ * Output for the Search API.
+ */
+export interface SearchResult {
+ /**
+ * A list of WebSearchResult objects, ordered by decreasing relevance.
+ */
+ results: Array;
+
+ /**
+ * Search ID. Example: `search_cad0a6d2-dec0-46bd-95ae-900527d880e7`
+ */
+ search_id: string;
+}
+
+/**
+ * A single search result from the web search API.
+ */
+export interface WebSearchResult {
+ /**
+ * Text excerpts from the search result which are relevant to the request.
+ */
+ excerpts: Array;
+
+ /**
+ * Title of the search result.
+ */
+ title: string;
+
+ /**
+ * URL associated with the search result.
+ */
+ url: string;
+}
+
+export interface BetaSearchParams {
+ /**
+ * Upper bound on the number of characters to include in excerpts for each search
+ * result.
+ */
+ max_chars_per_result?: number | null;
+
+ /**
+ * Upper bound on the number of results to return. May be limited by the processor.
+ * Defaults to 10 if not provided.
+ */
+ max_results?: number | null;
+
+ /**
+ * Natural-language description of what the web search is trying to find. May
+ * include guidance about preferred sources or freshness. At least one of objective
+ * or search_queries must be provided.
+ */
+ objective?: string | null;
+
+ /**
+ * Search processor.
+ */
+ processor?: 'base' | 'pro';
+
+ /**
+ * Optional list of traditional keyword search queries to guide the search. May
+ * contain search operators. At least one of objective or search_queries must be
+ * provided.
+ */
+ search_queries?: Array | null;
+
+ /**
+ * Source policy for web search results.
+ *
+ * This policy governs which sources are allowed/disallowed in results.
+ */
+ source_policy?: Shared.SourcePolicy | null;
}
Beta.TaskRun = TaskRun;
-Beta.TaskGroup = TaskGroup;
export declare namespace Beta {
- export { TaskRun as TaskRun };
+ export {
+ type SearchResult as SearchResult,
+ type WebSearchResult as WebSearchResult,
+ type BetaSearchParams as BetaSearchParams,
+ };
+
+ export {
+ TaskRun as TaskRun,
+ type BetaRunInput as BetaRunInput,
+ type BetaTaskRunResult as BetaTaskRunResult,
+ type ErrorEvent as ErrorEvent,
+ type McpServer as McpServer,
+ type McpToolCall as McpToolCall,
+ type ParallelBeta as ParallelBeta,
+ type TaskRunEvent as TaskRunEvent,
+ type Webhook as Webhook,
+ type TaskRunEventsResponse as TaskRunEventsResponse,
+ type TaskRunCreateParams as TaskRunCreateParams,
+ type TaskRunResultParams as TaskRunResultParams,
+ };
- export { TaskGroup as TaskGroup };
+ export {
+ type TaskGroup as TaskGroup,
+ type TaskGroupRunResponse as TaskGroupRunResponse,
+ type TaskGroupStatus as TaskGroupStatus,
+ type TaskGroupEventsResponse as TaskGroupEventsResponse,
+ type TaskGroupGetRunsResponse as TaskGroupGetRunsResponse,
+ type TaskGroupCreateParams as TaskGroupCreateParams,
+ type TaskGroupAddRunsParams as TaskGroupAddRunsParams,
+ type TaskGroupEventsParams as TaskGroupEventsParams,
+ type TaskGroupGetRunsParams as TaskGroupGetRunsParams,
+ };
}
diff --git a/src/resources/beta/index.ts b/src/resources/beta/index.ts
index bf1f5d2..061e58c 100644
--- a/src/resources/beta/index.ts
+++ b/src/resources/beta/index.ts
@@ -1,5 +1,28 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
export { Beta } from './beta';
-export { TaskGroup } from './task-group';
-export { TaskRun } from './task-run';
+export {
+ TaskGroup,
+ type TaskGroupRunResponse,
+ type TaskGroupStatus,
+ type TaskGroupEventsResponse,
+ type TaskGroupGetRunsResponse,
+ type TaskGroupCreateParams,
+ type TaskGroupAddRunsParams,
+ type TaskGroupEventsParams,
+ type TaskGroupGetRunsParams,
+} from './task-group';
+export {
+ TaskRun,
+ type BetaRunInput,
+ type BetaTaskRunResult,
+ type ErrorEvent,
+ type McpServer,
+ type McpToolCall,
+ type ParallelBeta,
+ type TaskRunEvent,
+ type Webhook,
+ type TaskRunEventsResponse,
+ type TaskRunCreateParams,
+ type TaskRunResultParams,
+} from './task-run';
diff --git a/src/resources/beta/task-group.ts b/src/resources/beta/task-group.ts
index bfdaad5..f47f308 100644
--- a/src/resources/beta/task-group.ts
+++ b/src/resources/beta/task-group.ts
@@ -1,5 +1,270 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
import { APIResource } from '../../core/resource';
+import * as TaskGroupAPI from './task-group';
+import * as TaskRunAPI from '../task-run';
+import * as BetaTaskRunAPI from './task-run';
+import { APIPromise } from '../../core/api-promise';
+import { Stream } from '../../core/streaming';
+import { buildHeaders } from '../../internal/headers';
+import { RequestOptions } from '../../internal/request-options';
+import { path } from '../../internal/utils/path';
-export class TaskGroup extends APIResource {}
+export class TaskGroup extends APIResource {
+ /**
+ * Initiates a TaskGroup to group and track multiple runs.
+ */
+ create(body: TaskGroupCreateParams, options?: RequestOptions): APIPromise {
+ return this._client.post('/v1beta/tasks/groups', { body, ...options });
+ }
+
+ /**
+ * Retrieves aggregated status across runs in a TaskGroup.
+ */
+ retrieve(taskGroupID: string, options?: RequestOptions): APIPromise {
+ return this._client.get(path`/v1beta/tasks/groups/${taskGroupID}`, options);
+ }
+
+ /**
+ * Initiates multiple task runs within a TaskGroup.
+ */
+ addRuns(
+ taskGroupID: string,
+ params: TaskGroupAddRunsParams,
+ options?: RequestOptions,
+ ): APIPromise {
+ const { betas, ...body } = params;
+ return this._client.post(path`/v1beta/tasks/groups/${taskGroupID}/runs`, {
+ body,
+ ...options,
+ headers: buildHeaders([
+ { ...(betas?.toString() != null ? { 'parallel-beta': betas?.toString() } : undefined) },
+ options?.headers,
+ ]),
+ });
+ }
+
+ /**
+ * Streams events from a TaskGroup: status updates and run completions.
+ *
+ * The connection will remain open for up to 10 minutes as long as at least one run
+ * in the TaskGroup is active.
+ */
+ events(
+ taskGroupID: string,
+ query: TaskGroupEventsParams | undefined = {},
+ options?: RequestOptions,
+ ): APIPromise> {
+ return this._client.get(path`/v1beta/tasks/groups/${taskGroupID}/events`, {
+ query,
+ ...options,
+ headers: buildHeaders([{ Accept: 'text/event-stream' }, options?.headers]),
+ stream: true,
+ }) as APIPromise>;
+ }
+
+ /**
+ * Retrieves task runs in a TaskGroup and optionally their inputs and outputs.
+ */
+ getRuns(
+ taskGroupID: string,
+ query: TaskGroupGetRunsParams | undefined = {},
+ options?: RequestOptions,
+ ): APIPromise> {
+ return this._client.get(path`/v1beta/tasks/groups/${taskGroupID}/runs`, {
+ query,
+ ...options,
+ headers: buildHeaders([{ Accept: 'text/event-stream' }, options?.headers]),
+ stream: true,
+ }) as APIPromise>;
+ }
+}
+
+/**
+ * Response object for a task group, including its status and metadata.
+ */
+export interface TaskGroup {
+ /**
+ * Timestamp of the creation of the group, as an RFC 3339 string.
+ */
+ created_at: string | null;
+
+ /**
+ * Status of a task group.
+ */
+ status: TaskGroupStatus;
+
+ /**
+ * ID of the group.
+ */
+ taskgroup_id: string;
+
+ /**
+ * User-provided metadata stored with the group.
+ */
+ metadata?: { [key: string]: string | number | boolean } | null;
+}
+
+/**
+ * Response from adding new task runs to a task group.
+ */
+export interface TaskGroupRunResponse {
+ /**
+ * Cursor for these runs in the event stream at
+ * taskgroup/events?last_event_id=. Empty for the first runs in the
+ * group.
+ */
+ event_cursor: string | null;
+
+ /**
+ * Cursor for these runs in the run stream at
+ * taskgroup/runs?last_event_id=. Empty for the first runs in the
+ * group.
+ */
+ run_cursor: string | null;
+
+ /**
+ * IDs of the newly created runs.
+ */
+ run_ids: Array;
+
+ /**
+ * Status of a task group.
+ */
+ status: TaskGroupStatus;
+}
+
+/**
+ * Status of a task group.
+ */
+export interface TaskGroupStatus {
+ /**
+ * True if at least one run in the group is currently active, i.e. status is one of
+ * {'cancelling', 'queued', 'running'}.
+ */
+ is_active: boolean;
+
+ /**
+ * Timestamp of the last status update to the group, as an RFC 3339 string.
+ */
+ modified_at: string | null;
+
+ /**
+ * Number of task runs in the group.
+ */
+ num_task_runs: number;
+
+ /**
+ * Human-readable status message for the group.
+ */
+ status_message: string | null;
+
+ /**
+ * Number of task runs with each status.
+ */
+ task_run_status_counts: { [key: string]: number };
+}
+
+/**
+ * Event indicating an update to group status.
+ */
+export type TaskGroupEventsResponse =
+ | TaskGroupEventsResponse.TaskGroupStatusEvent
+ | BetaTaskRunAPI.TaskRunEvent
+ | BetaTaskRunAPI.ErrorEvent;
+
+export namespace TaskGroupEventsResponse {
+ /**
+ * Event indicating an update to group status.
+ */
+ export interface TaskGroupStatusEvent {
+ /**
+ * Cursor to resume the event stream.
+ */
+ event_id: string;
+
+ /**
+ * Status of a task group.
+ */
+ status: TaskGroupAPI.TaskGroupStatus;
+
+ /**
+ * Event type; always 'task_group_status'.
+ */
+ type: 'task_group_status';
+ }
+}
+
+/**
+ * Event when a task run transitions to a non-active status.
+ *
+ * May indicate completion, cancellation, or failure.
+ */
+export type TaskGroupGetRunsResponse = BetaTaskRunAPI.TaskRunEvent | BetaTaskRunAPI.ErrorEvent;
+
+export interface TaskGroupCreateParams {
+ /**
+ * User-provided metadata stored with the task group.
+ */
+ metadata?: { [key: string]: string | number | boolean } | null;
+}
+
+export interface TaskGroupAddRunsParams {
+ /**
+ * Body param: List of task runs to execute.
+ */
+ inputs: Array;
+
+ /**
+ * Body param: Specification for a task.
+ *
+ * Auto output schemas can be specified by setting `output_schema={"type":"auto"}`.
+ * Not specifying a TaskSpec is the same as setting an auto output schema.
+ *
+ * For convenience bare strings are also accepted as input or output schemas.
+ */
+ default_task_spec?: TaskRunAPI.TaskSpec | null;
+
+ /**
+ * Header param: Optional header to specify the beta version(s) to enable.
+ */
+ betas?: Array;
+}
+
+export interface TaskGroupEventsParams {
+ last_event_id?: string | null;
+
+ timeout?: number | null;
+}
+
+export interface TaskGroupGetRunsParams {
+ include_input?: boolean;
+
+ include_output?: boolean;
+
+ last_event_id?: string | null;
+
+ status?:
+ | 'queued'
+ | 'action_required'
+ | 'running'
+ | 'completed'
+ | 'failed'
+ | 'cancelling'
+ | 'cancelled'
+ | null;
+}
+
+export declare namespace TaskGroup {
+ export {
+ type TaskGroup as TaskGroup,
+ type TaskGroupRunResponse as TaskGroupRunResponse,
+ type TaskGroupStatus as TaskGroupStatus,
+ type TaskGroupEventsResponse as TaskGroupEventsResponse,
+ type TaskGroupGetRunsResponse as TaskGroupGetRunsResponse,
+ type TaskGroupCreateParams as TaskGroupCreateParams,
+ type TaskGroupAddRunsParams as TaskGroupAddRunsParams,
+ type TaskGroupEventsParams as TaskGroupEventsParams,
+ type TaskGroupGetRunsParams as TaskGroupGetRunsParams,
+ };
+}
diff --git a/src/resources/beta/task-run.ts b/src/resources/beta/task-run.ts
index e42a175..bfaf20f 100644
--- a/src/resources/beta/task-run.ts
+++ b/src/resources/beta/task-run.ts
@@ -1,5 +1,517 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
import { APIResource } from '../../core/resource';
+import * as BetaTaskRunAPI from './task-run';
+import * as Shared from '../shared';
+import * as TaskRunAPI from '../task-run';
+import { APIPromise } from '../../core/api-promise';
+import { Stream } from '../../core/streaming';
+import { buildHeaders } from '../../internal/headers';
+import { RequestOptions } from '../../internal/request-options';
+import { path } from '../../internal/utils/path';
-export class TaskRun extends APIResource {}
+export class TaskRun extends APIResource {
+ /**
+ * Initiates a task run.
+ *
+ * Returns immediately with a run object in status 'queued'.
+ *
+ * Beta features can be enabled by setting the 'parallel-beta' header.
+ */
+ create(params: TaskRunCreateParams, options?: RequestOptions): APIPromise {
+ const { betas, ...body } = params;
+ return this._client.post('/v1/tasks/runs?beta=true', {
+ body,
+ ...options,
+ headers: buildHeaders([
+ { ...(betas?.toString() != null ? { 'parallel-beta': betas?.toString() } : undefined) },
+ options?.headers,
+ ]),
+ });
+ }
+
+ /**
+ * Streams events for a task run.
+ *
+ * Returns a stream of events showing progress updates and state changes for the
+ * task run.
+ *
+ * For task runs that did not have enable_events set to true during creation, the
+ * frequency of events will be reduced.
+ */
+ events(runID: string, options?: RequestOptions): APIPromise> {
+ return this._client.get(path`/v1beta/tasks/runs/${runID}/events`, {
+ ...options,
+ headers: buildHeaders([{ Accept: 'text/event-stream' }, options?.headers]),
+ stream: true,
+ }) as APIPromise>;
+ }
+
+ /**
+ * Retrieves a run result by run_id, blocking until the run is completed.
+ */
+ result(
+ runID: string,
+ params: TaskRunResultParams | null | undefined = {},
+ options?: RequestOptions,
+ ): APIPromise {
+ const { betas, ...query } = params ?? {};
+ return this._client.get(path`/v1/tasks/runs/${runID}/result?beta=true`, {
+ query,
+ ...options,
+ headers: buildHeaders([
+ { ...(betas?.toString() != null ? { 'parallel-beta': betas?.toString() } : undefined) },
+ options?.headers,
+ ]),
+ });
+ }
+}
+
+/**
+ * Task run input with additional beta fields.
+ */
+export interface BetaRunInput {
+ /**
+ * Input to the task, either text or a JSON object.
+ */
+ input: string | { [key: string]: unknown };
+
+ /**
+ * Processor to use for the task.
+ */
+ processor: string;
+
+ /**
+ * Controls tracking of task run execution progress. When set to true, progress
+ * events are recorded and can be accessed via the
+ * [Task Run events](https://platform.parallel.ai/api-reference) endpoint. When
+ * false, no progress events are tracked. Note that progress tracking cannot be
+ * enabled after a run has been created. The flag is set to true by default for
+ * premium processors (pro and above). This feature is not available via the Python
+ * SDK. To enable this feature in your API requests, specify the `parallel-beta`
+ * header with `events-sse-2025-07-24` value.
+ */
+ enable_events?: boolean | null;
+
+ /**
+ * Optional list of MCP servers to use for the run. This feature is not available
+ * via the Python SDK. To enable this feature in your API requests, specify the
+ * `parallel-beta` header with `mcp-server-2025-07-17` value.
+ */
+ mcp_servers?: Array | null;
+
+ /**
+ * User-provided metadata stored with the run. Keys and values must be strings with
+ * a maximum length of 16 and 512 characters respectively.
+ */
+ metadata?: { [key: string]: string | number | boolean } | null;
+
+ /**
+ * Source policy for web search results.
+ *
+ * This policy governs which sources are allowed/disallowed in results.
+ */
+ source_policy?: Shared.SourcePolicy | null;
+
+ /**
+ * Specification for a task.
+ *
+ * Auto output schemas can be specified by setting `output_schema={"type":"auto"}`.
+ * Not specifying a TaskSpec is the same as setting an auto output schema.
+ *
+ * For convenience bare strings are also accepted as input or output schemas.
+ */
+ task_spec?: TaskRunAPI.TaskSpec | null;
+
+ /**
+ * Webhooks for Task Runs.
+ */
+ webhook?: Webhook | null;
+}
+
+/**
+ * Result of a beta task run. Available only if beta headers are specified.
+ */
+export interface BetaTaskRunResult {
+ /**
+ * Output from the task conforming to the output schema.
+ */
+ output: BetaTaskRunResult.BetaTaskRunTextOutput | BetaTaskRunResult.BetaTaskRunJsonOutput;
+
+ /**
+ * Status of a task run.
+ */
+ run: TaskRunAPI.TaskRun;
+}
+
+export namespace BetaTaskRunResult {
+ /**
+ * Output from a task that returns text.
+ */
+ export interface BetaTaskRunTextOutput {
+ /**
+ * Basis for the output.
+ */
+ basis: Array;
+
+ /**
+ * Text output from the task.
+ */
+ content: string;
+
+ /**
+ * The type of output being returned, as determined by the output schema of the
+ * task spec.
+ */
+ type: 'text';
+
+ /**
+ * Always None.
+ */
+ beta_fields?: { [key: string]: unknown } | null;
+
+ /**
+ * MCP tool calls made by the task.
+ */
+ mcp_tool_calls?: Array | null;
+ }
+
+ /**
+ * Output from a task that returns JSON.
+ */
+ export interface BetaTaskRunJsonOutput {
+ /**
+ * Basis for the output.
+ */
+ basis: Array;
+
+ /**
+ * Output from the task as a native JSON object, as determined by the output schema
+ * of the task spec.
+ */
+ content: { [key: string]: unknown };
+
+ /**
+ * The type of output being returned, as determined by the output schema of the
+ * task spec.
+ */
+ type: 'json';
+
+ /**
+ * Always None.
+ */
+ beta_fields?: { [key: string]: unknown } | null;
+
+ /**
+ * MCP tool calls made by the task.
+ */
+ mcp_tool_calls?: Array | null;
+
+ /**
+ * Output schema for the Task Run. Populated only if the task was executed with an
+ * auto schema.
+ */
+ output_schema?: { [key: string]: unknown } | null;
+ }
+}
+
+/**
+ * Event indicating an error.
+ */
+export interface ErrorEvent {
+ /**
+ * An error message.
+ */
+ error: Shared.ErrorObject;
+
+ /**
+ * Event type; always 'error'.
+ */
+ type: 'error';
+}
+
+/**
+ * MCP server configuration.
+ */
+export interface McpServer {
+ /**
+ * Name of the MCP server.
+ */
+ name: string;
+
+ /**
+ * URL of the MCP server.
+ */
+ url: string;
+
+ /**
+ * List of allowed tools for the MCP server.
+ */
+ allowed_tools?: Array | null;
+
+ /**
+ * Headers for the MCP server.
+ */
+ headers?: { [key: string]: string } | null;
+
+ /**
+ * Type of MCP server being configured. Always `url`.
+ */
+ type?: 'url';
+}
+
+/**
+ * Result of an MCP tool call.
+ */
+export interface McpToolCall {
+ /**
+ * Arguments used to call the MCP tool.
+ */
+ arguments: string;
+
+ /**
+ * Name of the MCP server.
+ */
+ server_name: string;
+
+ /**
+ * Identifier for the tool call.
+ */
+ tool_call_id: string;
+
+ /**
+ * Name of the tool being called.
+ */
+ tool_name: string;
+
+ /**
+ * Output received from the tool call, if successful.
+ */
+ content?: string | null;
+
+ /**
+ * Error message if the tool call failed.
+ */
+ error?: string | null;
+}
+
+/**
+ * Model for the parallel-beta header.
+ */
+export type ParallelBeta =
+ | 'mcp-server-2025-07-17'
+ | 'events-sse-2025-07-24'
+ | 'webhook-2025-08-12'
+ | (string & {});
+
+/**
+ * Event when a task run transitions to a non-active status.
+ *
+ * May indicate completion, cancellation, or failure.
+ */
+export interface TaskRunEvent {
+ /**
+ * Cursor to resume the event stream. Always empty for non Task Group runs.
+ */
+ event_id: string | null;
+
+ /**
+ * Status of a task run.
+ */
+ run: TaskRunAPI.TaskRun;
+
+ /**
+ * Event type; always 'task_run.state'.
+ */
+ type: 'task_run.state';
+
+ /**
+ * Task run input with additional beta fields.
+ */
+ input?: BetaRunInput | null;
+
+ /**
+ * Output from the run; included only if requested and if status == `completed`.
+ */
+ output?: TaskRunAPI.TaskRunTextOutput | TaskRunAPI.TaskRunJsonOutput | null;
+}
+
+/**
+ * Webhooks for Task Runs.
+ */
+export interface Webhook {
+ /**
+ * URL for the webhook.
+ */
+ url: string;
+
+ /**
+ * Event types to send the webhook notifications for.
+ */
+ event_types?: Array<'task_run.status'>;
+}
+
+/**
+ * A progress update for a task run.
+ */
+export type TaskRunEventsResponse =
+ | TaskRunEventsResponse.TaskRunProgressStatsEvent
+ | TaskRunEventsResponse.TaskRunProgressMessageEvent
+ | TaskRunEvent
+ | ErrorEvent;
+
+export namespace TaskRunEventsResponse {
+ /**
+ * A progress update for a task run.
+ */
+ export interface TaskRunProgressStatsEvent {
+ /**
+ * Source stats for a task run.
+ */
+ source_stats: TaskRunProgressStatsEvent.SourceStats;
+
+ /**
+ * Event type; always 'task_run.progress_stats'.
+ */
+ type: 'task_run.progress_stats';
+ }
+
+ export namespace TaskRunProgressStatsEvent {
+ /**
+ * Source stats for a task run.
+ */
+ export interface SourceStats {
+ /**
+ * Number of sources considered in processing the task.
+ */
+ num_sources_considered: number | null;
+
+ /**
+ * Number of sources read in processing the task.
+ */
+ num_sources_read: number | null;
+
+ /**
+ * A sample of URLs of sources read in processing the task.
+ */
+ sources_read_sample: Array | null;
+ }
+ }
+
+ /**
+ * A message for a task run progress update.
+ */
+ export interface TaskRunProgressMessageEvent {
+ /**
+ * Progress update message.
+ */
+ message: string;
+
+ /**
+ * Timestamp of the message.
+ */
+ timestamp: string | null;
+
+ /**
+ * Event type; always starts with 'task_run.progress_msg'.
+ */
+ type:
+ | 'task_run.progress_msg.plan'
+ | 'task_run.progress_msg.search'
+ | 'task_run.progress_msg.result'
+ | 'task_run.progress_msg.tool_call'
+ | 'task_run.progress_msg.exec_status';
+ }
+}
+
+export interface TaskRunCreateParams {
+ /**
+ * Body param: Input to the task, either text or a JSON object.
+ */
+ input: string | { [key: string]: unknown };
+
+ /**
+ * Body param: Processor to use for the task.
+ */
+ processor: string;
+
+ /**
+ * Body param: Controls tracking of task run execution progress. When set to true,
+ * progress events are recorded and can be accessed via the
+ * [Task Run events](https://platform.parallel.ai/api-reference) endpoint. When
+ * false, no progress events are tracked. Note that progress tracking cannot be
+ * enabled after a run has been created. The flag is set to true by default for
+ * premium processors (pro and above). This feature is not available via the Python
+ * SDK. To enable this feature in your API requests, specify the `parallel-beta`
+ * header with `events-sse-2025-07-24` value.
+ */
+ enable_events?: boolean | null;
+
+ /**
+ * Body param: Optional list of MCP servers to use for the run. This feature is not
+ * available via the Python SDK. To enable this feature in your API requests,
+ * specify the `parallel-beta` header with `mcp-server-2025-07-17` value.
+ */
+ mcp_servers?: Array | null;
+
+ /**
+ * Body param: User-provided metadata stored with the run. Keys and values must be
+ * strings with a maximum length of 16 and 512 characters respectively.
+ */
+ metadata?: { [key: string]: string | number | boolean } | null;
+
+ /**
+ * Body param: Source policy for web search results.
+ *
+ * This policy governs which sources are allowed/disallowed in results.
+ */
+ source_policy?: Shared.SourcePolicy | null;
+
+ /**
+ * Body param: Specification for a task.
+ *
+ * Auto output schemas can be specified by setting `output_schema={"type":"auto"}`.
+ * Not specifying a TaskSpec is the same as setting an auto output schema.
+ *
+ * For convenience bare strings are also accepted as input or output schemas.
+ */
+ task_spec?: TaskRunAPI.TaskSpec | null;
+
+ /**
+ * Body param: Webhooks for Task Runs.
+ */
+ webhook?: Webhook | null;
+
+ /**
+ * Header param: Optional header to specify the beta version(s) to enable.
+ */
+ betas?: Array;
+}
+
+export interface TaskRunResultParams {
+ /**
+ * Query param:
+ */
+ timeout?: number;
+
+ /**
+ * Header param: Optional header to specify the beta version(s) to enable.
+ */
+ betas?: Array;
+}
+
+export declare namespace TaskRun {
+ export {
+ type BetaRunInput as BetaRunInput,
+ type BetaTaskRunResult as BetaTaskRunResult,
+ type ErrorEvent as ErrorEvent,
+ type McpServer as McpServer,
+ type McpToolCall as McpToolCall,
+ type ParallelBeta as ParallelBeta,
+ type TaskRunEvent as TaskRunEvent,
+ type Webhook as Webhook,
+ type TaskRunEventsResponse as TaskRunEventsResponse,
+ type TaskRunCreateParams as TaskRunCreateParams,
+ type TaskRunResultParams as TaskRunResultParams,
+ };
+}
diff --git a/src/resources/index.ts b/src/resources/index.ts
index 911d9ca..709471e 100644
--- a/src/resources/index.ts
+++ b/src/resources/index.ts
@@ -4,8 +4,11 @@ export * from './shared';
export { Beta } from './beta/beta';
export {
TaskRun,
+ type AutoSchema,
type Citation,
+ type FieldBasis,
type JsonSchema,
+ type RunInput,
type TaskRunJsonOutput,
type TaskRunResult,
type TaskRunTextOutput,
diff --git a/src/resources/shared.ts b/src/resources/shared.ts
index 0ec2535..5bb1c1a 100644
--- a/src/resources/shared.ts
+++ b/src/resources/shared.ts
@@ -17,7 +17,7 @@ export interface ErrorObject {
/**
* Optional detail supporting the error.
*/
- detail?: unknown | null;
+ detail?: { [key: string]: unknown } | null;
}
/**
@@ -32,5 +32,45 @@ export interface ErrorResponse {
/**
* Always 'error'.
*/
- type?: 'error';
+ type: 'error';
+}
+
+/**
+ * Source policy for web search results.
+ *
+ * This policy governs which sources are allowed/disallowed in results.
+ */
+export interface SourcePolicy {
+ /**
+ * List of domains to exclude from results. If specified, sources from these
+ * domains will be excluded.
+ */
+ exclude_domains?: Array;
+
+ /**
+ * List of domains to restrict the results to. If specified, only sources from
+ * these domains will be included.
+ */
+ include_domains?: Array;
+}
+
+/**
+ * Human-readable message for a task.
+ */
+export interface Warning {
+ /**
+ * Human-readable message.
+ */
+ message: string;
+
+ /**
+ * Type of warning. Note that adding new warning types is considered a
+ * backward-compatible change.
+ */
+ type: 'spec_validation_warning' | 'input_validation_warning' | 'warning';
+
+ /**
+ * Optional detail supporting the warning.
+ */
+ detail?: { [key: string]: unknown } | null;
}
diff --git a/src/resources/task-run.ts b/src/resources/task-run.ts
index bac8e38..6cada92 100644
--- a/src/resources/task-run.ts
+++ b/src/resources/task-run.ts
@@ -1,28 +1,34 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
import { APIResource } from '../core/resource';
-import * as TaskRunAPI from './task-run';
+import * as Shared from './shared';
import { APIPromise } from '../core/api-promise';
import { RequestOptions } from '../internal/request-options';
import { path } from '../internal/utils/path';
export class TaskRun extends APIResource {
/**
- * Initiates a single task run.
+ * Initiates a task run.
+ *
+ * Returns immediately with a run object in status 'queued'.
+ *
+ * Beta features can be enabled by setting the 'parallel-beta' header.
*/
create(body: TaskRunCreateParams, options?: RequestOptions): APIPromise {
return this._client.post('/v1/tasks/runs', { body, ...options });
}
/**
- * Retrieves a run by run_id.
+ * Retrieves run status by run_id.
+ *
+ * The run result is available from the `/result` endpoint.
*/
retrieve(runID: string, options?: RequestOptions): APIPromise {
return this._client.get(path`/v1/tasks/runs/${runID}`, options);
}
/**
- * Retrieves a run by run_id, blocking until the run is completed.
+ * Retrieves a run result by run_id, blocking until the run is completed.
*/
result(
runID: string,
@@ -33,6 +39,16 @@ export class TaskRun extends APIResource {
}
}
+/**
+ * Auto schema for a task input or output.
+ */
+export interface AutoSchema {
+ /**
+ * The type of schema being defined. Always `auto`.
+ */
+ type?: 'auto';
+}
+
/**
* A citation for a task output.
*/
@@ -54,6 +70,32 @@ export interface Citation {
title?: string | null;
}
+/**
+ * Citations and reasoning supporting one field of a task output.
+ */
+export interface FieldBasis {
+ /**
+ * Name of the output field.
+ */
+ field: string;
+
+ /**
+ * Reasoning for the output field.
+ */
+ reasoning: string;
+
+ /**
+ * List of citations supporting the output field.
+ */
+ citations?: Array;
+
+ /**
+ * Confidence level for the output field. Only certain processors provide
+ * confidence levels.
+ */
+ confidence?: string | null;
+}
+
/**
* JSON schema for a task input or output.
*/
@@ -61,7 +103,7 @@ export interface JsonSchema {
/**
* A JSON Schema object. Only a subset of JSON Schema is supported.
*/
- json_schema: unknown;
+ json_schema: { [key: string]: unknown };
/**
* The type of schema being defined. Always `json`.
@@ -70,7 +112,45 @@ export interface JsonSchema {
}
/**
- * Status of a task.
+ * Request to run a task.
+ */
+export interface RunInput {
+ /**
+ * Input to the task, either text or a JSON object.
+ */
+ input: string | { [key: string]: unknown };
+
+ /**
+ * Processor to use for the task.
+ */
+ processor: string;
+
+ /**
+ * User-provided metadata stored with the run. Keys and values must be strings with
+ * a maximum length of 16 and 512 characters respectively.
+ */
+ metadata?: { [key: string]: string | number | boolean } | null;
+
+ /**
+ * Source policy for web search results.
+ *
+ * This policy governs which sources are allowed/disallowed in results.
+ */
+ source_policy?: Shared.SourcePolicy | null;
+
+ /**
+ * Specification for a task.
+ *
+ * Auto output schemas can be specified by setting `output_schema={"type":"auto"}`.
+ * Not specifying a TaskSpec is the same as setting an auto output schema.
+ *
+ * For convenience bare strings are also accepted as input or output schemas.
+ */
+ task_spec?: TaskSpec | null;
+}
+
+/**
+ * Status of a task run.
*/
export interface TaskRun {
/**
@@ -79,8 +159,8 @@ export interface TaskRun {
created_at: string | null;
/**
- * Whether the run is currently active; i.e. status is one of {'running', 'queued',
- * 'cancelling'}.
+ * Whether the run is currently active, i.e. status is one of {'cancelling',
+ * 'queued', 'running'}.
*/
is_active: boolean;
@@ -104,88 +184,67 @@ export interface TaskRun {
*/
status: 'queued' | 'action_required' | 'running' | 'completed' | 'failed' | 'cancelling' | 'cancelled';
+ /**
+ * An error message.
+ */
+ error?: Shared.ErrorObject | null;
+
/**
* User-provided metadata stored with the run.
*/
metadata?: { [key: string]: string | number | boolean } | null;
/**
- * Warnings for the run.
+ * ID of the taskgroup to which the run belongs.
*/
- warnings?: Array | null;
-}
+ taskgroup_id?: string | null;
-export namespace TaskRun {
/**
- * Human-readable message for a task.
+ * Warnings for the run, if any.
*/
- export interface Warning {
- /**
- * Human-readable message.
- */
- message: string;
-
- /**
- * Type of warning. Note that adding new warning types is considered a
- * backward-compatible change.
- */
- type: string;
-
- /**
- * Optional detail supporting the warning.
- */
- detail?: unknown | null;
- }
+ warnings?: Array | null;
}
/**
- * Output from a task that returns text.
+ * Output from a task that returns JSON.
*/
export interface TaskRunJsonOutput {
/**
* Basis for each top-level field in the JSON output.
*/
- basis: Array;
+ basis: Array;
/**
* Output from the task as a native JSON object, as determined by the output schema
* of the task spec.
*/
- content: unknown;
+ content: { [key: string]: unknown };
/**
* The type of output being returned, as determined by the output schema of the
* task spec.
*/
type: 'json';
-}
-export namespace TaskRunJsonOutput {
/**
- * Citations and reasoning supporting one field of a task output.
+ * Additional fields from beta features used in this task run. When beta features
+ * are specified during both task run creation and result retrieval, this field
+ * will be empty and instead the relevant beta attributes will be directly included
+ * in the `BetaTaskRunJsonOutput` or corresponding output type. However, if beta
+ * features were specified during task run creation but not during result
+ * retrieval, this field will contain the dump of fields from those beta features.
+ * Each key represents the beta feature version (one amongst parallel-beta headers)
+ * and the values correspond to the beta feature attributes, if any. For now, only
+ * MCP server beta features have attributes. For example,
+ * `{mcp-server-2025-07-17: [{'server_name':'mcp_server', 'tool_call_id': 'tc_123', ...}]}}`
*/
- export interface Basis {
- /**
- * Name of the output field.
- */
- field: string;
-
- /**
- * Reasoning for the output field.
- */
- reasoning: string;
-
- /**
- * List of citations supporting the output field.
- */
- citations?: Array;
+ beta_fields?: { [key: string]: unknown } | null;
- /**
- * Confidence level for the output field. Only certain processors provide
- * confidence levels.
- */
- confidence?: string | null;
- }
+ /**
+ * Output schema for the Task Run. Populated only if the task was executed with an
+ * auto schema.
+ */
+ output_schema?: { [key: string]: unknown } | null;
}
/**
@@ -198,7 +257,7 @@ export interface TaskRunResult {
output: TaskRunTextOutput | TaskRunJsonOutput;
/**
- * Status of a task.
+ * Status of a task run.
*/
run: TaskRun;
}
@@ -210,7 +269,7 @@ export interface TaskRunTextOutput {
/**
* Basis for the output. The basis has a single field 'output'.
*/
- basis: Array;
+ basis: Array;
/**
* Text output from the task.
@@ -222,41 +281,29 @@ export interface TaskRunTextOutput {
* task spec.
*/
type: 'text';
-}
-export namespace TaskRunTextOutput {
/**
- * Citations and reasoning supporting one field of a task output.
+ * Additional fields from beta features used in this task run. When beta features
+ * are specified during both task run creation and result retrieval, this field
+ * will be empty and instead the relevant beta attributes will be directly included
+ * in the `BetaTaskRunJsonOutput` or corresponding output type. However, if beta
+ * features were specified during task run creation but not during result
+ * retrieval, this field will contain the dump of fields from those beta features.
+ * Each key represents the beta feature version (one amongst parallel-beta headers)
+ * and the values correspond to the beta feature attributes, if any. For now, only
+ * MCP server beta features have attributes. For example,
+ * `{mcp-server-2025-07-17: [{'server_name':'mcp_server', 'tool_call_id': 'tc_123', ...}]}}`
*/
- export interface Basis {
- /**
- * Name of the output field.
- */
- field: string;
-
- /**
- * Reasoning for the output field.
- */
- reasoning: string;
-
- /**
- * List of citations supporting the output field.
- */
- citations?: Array;
-
- /**
- * Confidence level for the output field. Only certain processors provide
- * confidence levels.
- */
- confidence?: string | null;
- }
+ beta_fields?: { [key: string]: unknown } | null;
}
/**
* Specification for a task.
*
- * For convenience we allow bare strings as input or output schemas, which is
- * equivalent to a text schema with the same description.
+ * Auto output schemas can be specified by setting `output_schema={"type":"auto"}`.
+ * Not specifying a TaskSpec is the same as setting an auto output schema.
+ *
+ * For convenience bare strings are also accepted as input or output schemas.
*/
export interface TaskSpec {
/**
@@ -265,13 +312,13 @@ export interface TaskSpec {
* response. A bare string is equivalent to a text schema with the same
* description.
*/
- output_schema: JsonSchema | TextSchema | string;
+ output_schema: JsonSchema | TextSchema | AutoSchema | string;
/**
* Optional JSON schema or text description of expected input to the task. A bare
* string is equivalent to a text schema with the same description.
*/
- input_schema?: JsonSchema | TextSchema | string | null;
+ input_schema?: string | JsonSchema | TextSchema | null;
}
/**
@@ -293,7 +340,7 @@ export interface TaskRunCreateParams {
/**
* Input to the task, either text or a JSON object.
*/
- input: string | unknown;
+ input: string | { [key: string]: unknown };
/**
* Processor to use for the task.
@@ -306,11 +353,20 @@ export interface TaskRunCreateParams {
*/
metadata?: { [key: string]: string | number | boolean } | null;
+ /**
+ * Source policy for web search results.
+ *
+ * This policy governs which sources are allowed/disallowed in results.
+ */
+ source_policy?: Shared.SourcePolicy | null;
+
/**
* Specification for a task.
*
- * For convenience we allow bare strings as input or output schemas, which is
- * equivalent to a text schema with the same description.
+ * Auto output schemas can be specified by setting `output_schema={"type":"auto"}`.
+ * Not specifying a TaskSpec is the same as setting an auto output schema.
+ *
+ * For convenience bare strings are also accepted as input or output schemas.
*/
task_spec?: TaskSpec | null;
}
@@ -321,8 +377,11 @@ export interface TaskRunResultParams {
export declare namespace TaskRun {
export {
+ type AutoSchema as AutoSchema,
type Citation as Citation,
+ type FieldBasis as FieldBasis,
type JsonSchema as JsonSchema,
+ type RunInput as RunInput,
type TaskRun as TaskRun,
type TaskRunJsonOutput as TaskRunJsonOutput,
type TaskRunResult as TaskRunResult,
diff --git a/src/streaming.ts b/src/streaming.ts
new file mode 100644
index 0000000..9e6da10
--- /dev/null
+++ b/src/streaming.ts
@@ -0,0 +1,2 @@
+/** @deprecated Import from ./core/streaming instead */
+export * from './core/streaming';
diff --git a/tests/api-resources/beta/beta.test.ts b/tests/api-resources/beta/beta.test.ts
new file mode 100644
index 0000000..f6a03e3
--- /dev/null
+++ b/tests/api-resources/beta/beta.test.ts
@@ -0,0 +1,21 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Parallel from 'parallel-web';
+
+const client = new Parallel({
+ apiKey: 'My API Key',
+ baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource beta', () => {
+ test('search', async () => {
+ const responsePromise = client.beta.search({});
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+});
diff --git a/tests/api-resources/beta/task-group.test.ts b/tests/api-resources/beta/task-group.test.ts
new file mode 100644
index 0000000..612b8bc
--- /dev/null
+++ b/tests/api-resources/beta/task-group.test.ts
@@ -0,0 +1,126 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Parallel from 'parallel-web';
+
+const client = new Parallel({
+ apiKey: 'My API Key',
+ baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource taskGroup', () => {
+ test('create', async () => {
+ const responsePromise = client.beta.taskGroup.create({});
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('retrieve', async () => {
+ const responsePromise = client.beta.taskGroup.retrieve('taskgroup_id');
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('addRuns: only required params', async () => {
+ const responsePromise = client.beta.taskGroup.addRuns('taskgroup_id', {
+ inputs: [{ input: 'What was the GDP of France in 2023?', processor: 'base' }],
+ });
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('addRuns: required and optional params', async () => {
+ const response = await client.beta.taskGroup.addRuns('taskgroup_id', {
+ inputs: [
+ {
+ input: 'What was the GDP of France in 2023?',
+ processor: 'base',
+ enable_events: true,
+ mcp_servers: [
+ { name: 'name', url: 'url', allowed_tools: ['string'], headers: { foo: 'string' }, type: 'url' },
+ ],
+ metadata: { foo: 'string' },
+ source_policy: { exclude_domains: ['string'], include_domains: ['string'] },
+ task_spec: {
+ output_schema: {
+ json_schema: { additionalProperties: 'bar', properties: 'bar', required: 'bar', type: 'bar' },
+ type: 'json',
+ },
+ input_schema: 'string',
+ },
+ webhook: { url: 'url', event_types: ['task_run.status'] },
+ },
+ ],
+ default_task_spec: {
+ output_schema: {
+ json_schema: { additionalProperties: 'bar', properties: 'bar', required: 'bar', type: 'bar' },
+ type: 'json',
+ },
+ input_schema: 'string',
+ },
+ betas: ['mcp-server-2025-07-17'],
+ });
+ });
+
+ // Prism doesn't support text/event-stream responses
+ test.skip('events', async () => {
+ const responsePromise = client.beta.taskGroup.events('taskgroup_id');
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ // Prism doesn't support text/event-stream responses
+ test.skip('events: request options and params are passed correctly', async () => {
+ // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
+ await expect(
+ client.beta.taskGroup.events(
+ 'taskgroup_id',
+ { last_event_id: 'last_event_id', timeout: 0 },
+ { path: '/_stainless_unknown_path' },
+ ),
+ ).rejects.toThrow(Parallel.NotFoundError);
+ });
+
+ // Prism doesn't support text/event-stream responses
+ test.skip('getRuns', async () => {
+ const responsePromise = client.beta.taskGroup.getRuns('taskgroup_id');
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ // Prism doesn't support text/event-stream responses
+ test.skip('getRuns: request options and params are passed correctly', async () => {
+ // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
+ await expect(
+ client.beta.taskGroup.getRuns(
+ 'taskgroup_id',
+ { include_input: true, include_output: true, last_event_id: 'last_event_id', status: 'queued' },
+ { path: '/_stainless_unknown_path' },
+ ),
+ ).rejects.toThrow(Parallel.NotFoundError);
+ });
+});
diff --git a/tests/api-resources/beta/task-run.test.ts b/tests/api-resources/beta/task-run.test.ts
new file mode 100644
index 0000000..29adaa3
--- /dev/null
+++ b/tests/api-resources/beta/task-run.test.ts
@@ -0,0 +1,80 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Parallel from 'parallel-web';
+
+const client = new Parallel({
+ apiKey: 'My API Key',
+ baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource taskRun', () => {
+ test('create: only required params', async () => {
+ const responsePromise = client.beta.taskRun.create({
+ input: 'What was the GDP of France in 2023?',
+ processor: 'base',
+ });
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('create: required and optional params', async () => {
+ const response = await client.beta.taskRun.create({
+ input: 'What was the GDP of France in 2023?',
+ processor: 'base',
+ enable_events: true,
+ mcp_servers: [
+ { name: 'name', url: 'url', allowed_tools: ['string'], headers: { foo: 'string' }, type: 'url' },
+ ],
+ metadata: { foo: 'string' },
+ source_policy: { exclude_domains: ['string'], include_domains: ['string'] },
+ task_spec: {
+ output_schema: {
+ json_schema: { additionalProperties: 'bar', properties: 'bar', required: 'bar', type: 'bar' },
+ type: 'json',
+ },
+ input_schema: 'string',
+ },
+ webhook: { url: 'url', event_types: ['task_run.status'] },
+ betas: ['mcp-server-2025-07-17'],
+ });
+ });
+
+ // Prism doesn't support text/event-stream responses
+ test.skip('events', async () => {
+ const responsePromise = client.beta.taskRun.events('run_id');
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('result', async () => {
+ const responsePromise = client.beta.taskRun.result('run_id');
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('result: request options and params are passed correctly', async () => {
+ // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
+ await expect(
+ client.beta.taskRun.result(
+ 'run_id',
+ { timeout: 0, betas: ['mcp-server-2025-07-17'] },
+ { path: '/_stainless_unknown_path' },
+ ),
+ ).rejects.toThrow(Parallel.NotFoundError);
+ });
+});
diff --git a/tests/api-resources/task-run.test.ts b/tests/api-resources/task-run.test.ts
index be6e12b..eedd2bb 100644
--- a/tests/api-resources/task-run.test.ts
+++ b/tests/api-resources/task-run.test.ts
@@ -9,7 +9,10 @@ const client = new Parallel({
describe('resource taskRun', () => {
test('create: only required params', async () => {
- const responsePromise = client.taskRun.create({ input: 'France (2023)', processor: 'processor' });
+ const responsePromise = client.taskRun.create({
+ input: 'What was the GDP of France in 2023?',
+ processor: 'base',
+ });
const rawResponse = await responsePromise.asResponse();
expect(rawResponse).toBeInstanceOf(Response);
const response = await responsePromise;
@@ -21,38 +24,16 @@ describe('resource taskRun', () => {
test('create: required and optional params', async () => {
const response = await client.taskRun.create({
- input: 'France (2023)',
- processor: 'processor',
+ input: 'What was the GDP of France in 2023?',
+ processor: 'base',
metadata: { foo: 'string' },
+ source_policy: { exclude_domains: ['string'], include_domains: ['string'] },
task_spec: {
output_schema: {
- json_schema: {
- additionalProperties: false,
- properties: {
- gdp: {
- description: "GDP in USD for the year, formatted like '$3.1 trillion (2023)'",
- type: 'string',
- },
- },
- required: ['gdp'],
- type: 'object',
- },
- type: 'json',
- },
- input_schema: {
- json_schema: {
- additionalProperties: false,
- properties: {
- gdp: {
- description: "GDP in USD for the year, formatted like '$3.1 trillion (2023)'",
- type: 'string',
- },
- },
- required: ['gdp'],
- type: 'object',
- },
+ json_schema: { additionalProperties: 'bar', properties: 'bar', required: 'bar', type: 'bar' },
type: 'json',
},
+ input_schema: 'string',
},
});
});
diff --git a/tests/internal/decoders/line.test.ts b/tests/internal/decoders/line.test.ts
new file mode 100644
index 0000000..13e60b9
--- /dev/null
+++ b/tests/internal/decoders/line.test.ts
@@ -0,0 +1,128 @@
+import { findDoubleNewlineIndex, LineDecoder } from 'parallel-web/internal/decoders/line';
+
+function decodeChunks(chunks: string[], { flush }: { flush: boolean } = { flush: false }): string[] {
+ const decoder = new LineDecoder();
+ const lines: string[] = [];
+ for (const chunk of chunks) {
+ lines.push(...decoder.decode(chunk));
+ }
+
+ if (flush) {
+ lines.push(...decoder.flush());
+ }
+
+ return lines;
+}
+
+describe('line decoder', () => {
+ test('basic', () => {
+ // baz is not included because the line hasn't ended yet
+ expect(decodeChunks(['foo', ' bar\nbaz'])).toEqual(['foo bar']);
+ });
+
+ test('basic with \\r', () => {
+ expect(decodeChunks(['foo', ' bar\r\nbaz'])).toEqual(['foo bar']);
+ expect(decodeChunks(['foo', ' bar\r\nbaz'], { flush: true })).toEqual(['foo bar', 'baz']);
+ });
+
+ test('trailing new lines', () => {
+ expect(decodeChunks(['foo', ' bar', 'baz\n', 'thing\n'])).toEqual(['foo barbaz', 'thing']);
+ });
+
+ test('trailing new lines with \\r', () => {
+ expect(decodeChunks(['foo', ' bar', 'baz\r\n', 'thing\r\n'])).toEqual(['foo barbaz', 'thing']);
+ });
+
+ test('escaped new lines', () => {
+ expect(decodeChunks(['foo', ' bar\\nbaz\n'])).toEqual(['foo bar\\nbaz']);
+ });
+
+ test('escaped new lines with \\r', () => {
+ expect(decodeChunks(['foo', ' bar\\r\\nbaz\n'])).toEqual(['foo bar\\r\\nbaz']);
+ });
+
+ test('\\r & \\n split across multiple chunks', () => {
+ expect(decodeChunks(['foo\r', '\n', 'bar'], { flush: true })).toEqual(['foo', 'bar']);
+ });
+
+ test('single \\r', () => {
+ expect(decodeChunks(['foo\r', 'bar'], { flush: true })).toEqual(['foo', 'bar']);
+ });
+
+ test('double \\r', () => {
+ expect(decodeChunks(['foo\r', 'bar\r'], { flush: true })).toEqual(['foo', 'bar']);
+ expect(decodeChunks(['foo\r', '\r', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']);
+ // implementation detail that we don't yield the single \r line until a new \r or \n is encountered
+ expect(decodeChunks(['foo\r', '\r', 'bar'], { flush: false })).toEqual(['foo']);
+ });
+
+ test('double \\r then \\r\\n', () => {
+ expect(decodeChunks(['foo\r', '\r', '\r', '\n', 'bar', '\n'])).toEqual(['foo', '', '', 'bar']);
+ expect(decodeChunks(['foo\n', '\n', '\n', 'bar', '\n'])).toEqual(['foo', '', '', 'bar']);
+ });
+
+ test('double newline', () => {
+ expect(decodeChunks(['foo\n\nbar'], { flush: true })).toEqual(['foo', '', 'bar']);
+ expect(decodeChunks(['foo', '\n', '\nbar'], { flush: true })).toEqual(['foo', '', 'bar']);
+ expect(decodeChunks(['foo\n', '\n', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']);
+ expect(decodeChunks(['foo', '\n', '\n', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']);
+ });
+
+ test('multi-byte characters across chunks', () => {
+ const decoder = new LineDecoder();
+
+ // bytes taken from the string 'известни' and arbitrarily split
+ // so that some multi-byte characters span multiple chunks
+ expect(decoder.decode(new Uint8Array([0xd0]))).toHaveLength(0);
+ expect(decoder.decode(new Uint8Array([0xb8, 0xd0, 0xb7, 0xd0]))).toHaveLength(0);
+ expect(
+ decoder.decode(new Uint8Array([0xb2, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82, 0xd0, 0xbd, 0xd0, 0xb8])),
+ ).toHaveLength(0);
+
+ const decoded = decoder.decode(new Uint8Array([0xa]));
+ expect(decoded).toEqual(['известни']);
+ });
+
+ test('flushing trailing newlines', () => {
+ expect(decodeChunks(['foo\n', '\nbar'], { flush: true })).toEqual(['foo', '', 'bar']);
+ });
+
+ test('flushing empty buffer', () => {
+ expect(decodeChunks([], { flush: true })).toEqual([]);
+ });
+});
+
+describe('findDoubleNewlineIndex', () => {
+ test('finds \\n\\n', () => {
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\n\nbar'))).toBe(5);
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('\n\nbar'))).toBe(2);
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\n\n'))).toBe(5);
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('\n\n'))).toBe(2);
+ });
+
+ test('finds \\r\\r', () => {
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\rbar'))).toBe(5);
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('\r\rbar'))).toBe(2);
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\r'))).toBe(5);
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('\r\r'))).toBe(2);
+ });
+
+ test('finds \\r\\n\\r\\n', () => {
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\n\r\nbar'))).toBe(7);
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('\r\n\r\nbar'))).toBe(4);
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\n\r\n'))).toBe(7);
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('\r\n\r\n'))).toBe(4);
+ });
+
+ test('returns -1 when no double newline found', () => {
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\nbar'))).toBe(-1);
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\rbar'))).toBe(-1);
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\nbar'))).toBe(-1);
+ expect(findDoubleNewlineIndex(new TextEncoder().encode(''))).toBe(-1);
+ });
+
+ test('handles incomplete patterns', () => {
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\n\r'))).toBe(-1);
+ expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\n'))).toBe(-1);
+ });
+});
diff --git a/tests/streaming.test.ts b/tests/streaming.test.ts
new file mode 100644
index 0000000..068d3af
--- /dev/null
+++ b/tests/streaming.test.ts
@@ -0,0 +1,219 @@
+import assert from 'assert';
+import { _iterSSEMessages } from 'parallel-web/core/streaming';
+import { ReadableStreamFrom } from 'parallel-web/internal/shims';
+
+describe('streaming decoding', () => {
+ test('basic', async () => {
+ async function* body(): AsyncGenerator {
+ yield Buffer.from('event: completion\n');
+ yield Buffer.from('data: {"foo":true}\n');
+ yield Buffer.from('\n');
+ }
+
+ const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
+ Symbol.asyncIterator
+ ]();
+
+ let event = await stream.next();
+ assert(event.value);
+ expect(JSON.parse(event.value.data)).toEqual({ foo: true });
+
+ event = await stream.next();
+ expect(event.done).toBeTruthy();
+ });
+
+ test('data without event', async () => {
+ async function* body(): AsyncGenerator {
+ yield Buffer.from('data: {"foo":true}\n');
+ yield Buffer.from('\n');
+ }
+
+ const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
+ Symbol.asyncIterator
+ ]();
+
+ let event = await stream.next();
+ assert(event.value);
+ expect(event.value.event).toBeNull();
+ expect(JSON.parse(event.value.data)).toEqual({ foo: true });
+
+ event = await stream.next();
+ expect(event.done).toBeTruthy();
+ });
+
+ test('event without data', async () => {
+ async function* body(): AsyncGenerator {
+ yield Buffer.from('event: foo\n');
+ yield Buffer.from('\n');
+ }
+
+ const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
+ Symbol.asyncIterator
+ ]();
+
+ let event = await stream.next();
+ assert(event.value);
+ expect(event.value.event).toEqual('foo');
+ expect(event.value.data).toEqual('');
+
+ event = await stream.next();
+ expect(event.done).toBeTruthy();
+ });
+
+ test('multiple events', async () => {
+ async function* body(): AsyncGenerator {
+ yield Buffer.from('event: foo\n');
+ yield Buffer.from('\n');
+ yield Buffer.from('event: ping\n');
+ yield Buffer.from('\n');
+ }
+
+ const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
+ Symbol.asyncIterator
+ ]();
+
+ let event = await stream.next();
+ assert(event.value);
+ expect(event.value.event).toEqual('foo');
+ expect(event.value.data).toEqual('');
+
+ event = await stream.next();
+ assert(event.value);
+ expect(event.value.event).toEqual('ping');
+ expect(event.value.data).toEqual('');
+
+ event = await stream.next();
+ expect(event.done).toBeTruthy();
+ });
+
+ test('multiple events with data', async () => {
+ async function* body(): AsyncGenerator {
+ yield Buffer.from('event: foo\n');
+ yield Buffer.from('data: {"foo":true}\n');
+ yield Buffer.from('\n');
+ yield Buffer.from('event: ping\n');
+ yield Buffer.from('data: {"bar":false}\n');
+ yield Buffer.from('\n');
+ }
+
+ const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
+ Symbol.asyncIterator
+ ]();
+
+ let event = await stream.next();
+ assert(event.value);
+ expect(event.value.event).toEqual('foo');
+ expect(JSON.parse(event.value.data)).toEqual({ foo: true });
+
+ event = await stream.next();
+ assert(event.value);
+ expect(event.value.event).toEqual('ping');
+ expect(JSON.parse(event.value.data)).toEqual({ bar: false });
+
+ event = await stream.next();
+ expect(event.done).toBeTruthy();
+ });
+
+ test('multiple data lines with empty line', async () => {
+ async function* body(): AsyncGenerator {
+ yield Buffer.from('event: ping\n');
+ yield Buffer.from('data: {\n');
+ yield Buffer.from('data: "foo":\n');
+ yield Buffer.from('data: \n');
+ yield Buffer.from('data:\n');
+ yield Buffer.from('data: true}\n');
+ yield Buffer.from('\n\n');
+ }
+
+ const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
+ Symbol.asyncIterator
+ ]();
+
+ let event = await stream.next();
+ assert(event.value);
+ expect(event.value.event).toEqual('ping');
+ expect(JSON.parse(event.value.data)).toEqual({ foo: true });
+ expect(event.value.data).toEqual('{\n"foo":\n\n\ntrue}');
+
+ event = await stream.next();
+ expect(event.done).toBeTruthy();
+ });
+
+ test('data json escaped double new line', async () => {
+ async function* body(): AsyncGenerator {
+ yield Buffer.from('event: ping\n');
+ yield Buffer.from('data: {"foo": "my long\\n\\ncontent"}');
+ yield Buffer.from('\n\n');
+ }
+
+ const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
+ Symbol.asyncIterator
+ ]();
+
+ let event = await stream.next();
+ assert(event.value);
+ expect(event.value.event).toEqual('ping');
+ expect(JSON.parse(event.value.data)).toEqual({ foo: 'my long\n\ncontent' });
+
+ event = await stream.next();
+ expect(event.done).toBeTruthy();
+ });
+
+ test('special new line characters', async () => {
+ async function* body(): AsyncGenerator {
+ yield Buffer.from('data: {"content": "culpa "}\n');
+ yield Buffer.from('\n');
+ yield Buffer.from('data: {"content": "');
+ yield Buffer.from([0xe2, 0x80, 0xa8]);
+ yield Buffer.from('"}\n');
+ yield Buffer.from('\n');
+ yield Buffer.from('data: {"content": "foo"}\n');
+ yield Buffer.from('\n');
+ }
+
+ const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
+ Symbol.asyncIterator
+ ]();
+
+ let event = await stream.next();
+ assert(event.value);
+ expect(JSON.parse(event.value.data)).toEqual({ content: 'culpa ' });
+
+ event = await stream.next();
+ assert(event.value);
+ expect(JSON.parse(event.value.data)).toEqual({ content: Buffer.from([0xe2, 0x80, 0xa8]).toString() });
+
+ event = await stream.next();
+ assert(event.value);
+ expect(JSON.parse(event.value.data)).toEqual({ content: 'foo' });
+
+ event = await stream.next();
+ expect(event.done).toBeTruthy();
+ });
+
+ test('multi-byte characters across chunks', async () => {
+ async function* body(): AsyncGenerator {
+ yield Buffer.from('event: completion\n');
+ yield Buffer.from('data: {"content": "');
+ // bytes taken from the string 'известни' and arbitrarily split
+ // so that some multi-byte characters span multiple chunks
+ yield Buffer.from([0xd0]);
+ yield Buffer.from([0xb8, 0xd0, 0xb7, 0xd0]);
+ yield Buffer.from([0xb2, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82, 0xd0, 0xbd, 0xd0, 0xb8]);
+ yield Buffer.from('"}\n');
+ yield Buffer.from('\n');
+ }
+
+ const stream = _iterSSEMessages(new Response(ReadableStreamFrom(body())), new AbortController())[
+ Symbol.asyncIterator
+ ]();
+
+ let event = await stream.next();
+ assert(event.value);
+ expect(event.value.event).toEqual('completion');
+ expect(JSON.parse(event.value.data)).toEqual({ content: 'известни' });
+
+ event = await stream.next();
+ expect(event.done).toBeTruthy();
+ });
+});
From f4b9bff96e41c4e31546276be440fb8f721ef595 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 1 Sep 2025 18:29:17 +0000
Subject: [PATCH 3/4] feat(api): update via SDK Studio
---
.github/workflows/publish-npm.yml | 32 ++++++++++++++++++++++++++++
.github/workflows/release-doctor.yml | 1 +
.stats.yml | 2 +-
CONTRIBUTING.md | 14 ++++++++++++
README.md | 5 +----
bin/check-release-environment | 4 ++++
6 files changed, 53 insertions(+), 5 deletions(-)
create mode 100644 .github/workflows/publish-npm.yml
diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml
new file mode 100644
index 0000000..4132771
--- /dev/null
+++ b/.github/workflows/publish-npm.yml
@@ -0,0 +1,32 @@
+# This workflow is triggered when a GitHub release is created.
+# It can also be run manually to re-publish to NPM in case it failed for some reason.
+# You can run this workflow by navigating to https://www.github.com/parallel-web/parallel-sdk-typescript/actions/workflows/publish-npm.yml
+name: Publish NPM
+on:
+ workflow_dispatch:
+
+ release:
+ types: [published]
+
+jobs:
+ publish:
+ name: publish
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Node
+ uses: actions/setup-node@v3
+ with:
+ node-version: '20'
+
+ - name: Install dependencies
+ run: |
+ yarn install
+
+ - name: Publish to NPM
+ run: |
+ bash ./bin/publish-npm
+ env:
+ NPM_TOKEN: ${{ secrets.PARALLEL_NPM_TOKEN || secrets.NPM_TOKEN }}
diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml
index 6ccfd4d..ef694b6 100644
--- a/.github/workflows/release-doctor.yml
+++ b/.github/workflows/release-doctor.yml
@@ -18,3 +18,4 @@ jobs:
run: |
bash ./bin/check-release-environment
env:
+ NPM_TOKEN: ${{ secrets.PARALLEL_NPM_TOKEN || secrets.NPM_TOKEN }}
diff --git a/.stats.yml b/.stats.yml
index 7c4f552..1835076 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 12
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/parallel-web%2Fparallel-sdk-1aeb1c81a84999f2d27ca9e86b041d74b892926bed126dc9b0f3cff4d7b26963.yml
openapi_spec_hash: 6280f6c6fb537f7c9ac5cc33ee2e433d
-config_hash: 451edf5a87ae14248aa336ffc08d216f
+config_hash: f9aa4d901581aaf70789dd0bc1b84597
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 661f881..51d29f4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -91,3 +91,17 @@ To format and fix all lint issues automatically:
```sh
$ yarn fix
```
+
+## Publishing and releases
+
+Changes made to this repository via the automated release PR pipeline should publish to npm automatically. If
+the changes aren't made through the automated pipeline, you may want to make releases manually.
+
+### Publish with a GitHub workflow
+
+You can release to package managers by using [the `Publish NPM` GitHub action](https://www.github.com/parallel-web/parallel-sdk-typescript/actions/workflows/publish-npm.yml). This requires a setup organization or repository secret to be set up.
+
+### Publish manually
+
+If you need to manually release a package, you can run the `bin/publish-npm` script with an `NPM_TOKEN` set on
+the environment.
diff --git a/README.md b/README.md
index 20d1935..1f86628 100644
--- a/README.md
+++ b/README.md
@@ -11,12 +11,9 @@ It is generated with [Stainless](https://www.stainless.com/).
## Installation
```sh
-npm install git+ssh://git@github.com:parallel-web/parallel-sdk-typescript.git
+npm install parallel-web
```
-> [!NOTE]
-> Once this package is [published to npm](https://www.stainless.com/docs/guides/publish), this will become: `npm install parallel-web`
-
## Usage
The full API of this library can be found in [api.md](api.md).
diff --git a/bin/check-release-environment b/bin/check-release-environment
index 6b43775..e4b6d58 100644
--- a/bin/check-release-environment
+++ b/bin/check-release-environment
@@ -2,6 +2,10 @@
errors=()
+if [ -z "${NPM_TOKEN}" ]; then
+ errors+=("The NPM_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets")
+fi
+
lenErrors=${#errors[@]}
if [[ lenErrors -gt 0 ]]; then
From 575a0ebf2d66b2abf238f64dd2e16c6bdd4eb451 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 1 Sep 2025 18:30:53 +0000
Subject: [PATCH 4/4] release: 0.1.0
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 65 +++++++++++++++++++++++++++++++++++
package.json | 2 +-
src/version.ts | 2 +-
4 files changed, 68 insertions(+), 3 deletions(-)
create mode 100644 CHANGELOG.md
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 67dcd73..466df71 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.0.1-alpha.0"
+ ".": "0.1.0"
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..8cf29d3
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,65 @@
+# Changelog
+
+## 0.1.0 (2025-09-01)
+
+Full Changelog: [v0.0.1-alpha.0...v0.1.0](https://github.com/parallel-web/parallel-sdk-typescript/compare/v0.0.1-alpha.0...v0.1.0)
+
+### Features
+
+* **api:** update via SDK Studio ([f4b9bff](https://github.com/parallel-web/parallel-sdk-typescript/commit/f4b9bff96e41c4e31546276be440fb8f721ef595))
+* **api:** update via SDK Studio ([7a985fb](https://github.com/parallel-web/parallel-sdk-typescript/commit/7a985fbf420eed89c87940cef40efb650b554c9b))
+* **api:** update via SDK Studio ([bea2209](https://github.com/parallel-web/parallel-sdk-typescript/commit/bea220934b8d11d97e35599dbc68ef3e69bb9523))
+* **api:** update via SDK Studio ([e555533](https://github.com/parallel-web/parallel-sdk-typescript/commit/e5555331ceb4722741248522936df737ab26176e))
+* **api:** update via SDK Studio ([e0922b7](https://github.com/parallel-web/parallel-sdk-typescript/commit/e0922b7ab408dd85495164111d53c3b9a1967da9))
+* **api:** update via SDK Studio ([496a13a](https://github.com/parallel-web/parallel-sdk-typescript/commit/496a13a6922fc072d62c34fa5fb9917824bee881))
+* **api:** update via SDK Studio ([bb43e98](https://github.com/parallel-web/parallel-sdk-typescript/commit/bb43e9850dcb30878dc67d8d5422917d951645b9))
+* **client:** add support for endpoint-specific base URLs ([6ca9e40](https://github.com/parallel-web/parallel-sdk-typescript/commit/6ca9e405e47920c42ab274da43a40f20864fa7c7))
+* **client:** add withOptions helper ([2dc5df5](https://github.com/parallel-web/parallel-sdk-typescript/commit/2dc5df50cfca28e611a360559c694889b9592939))
+
+
+### Bug Fixes
+
+* **client:** always overwrite when merging headers ([1b74500](https://github.com/parallel-web/parallel-sdk-typescript/commit/1b745008bbcff7583e8483a3989225fb6eb2719e))
+* **client:** explicitly copy fetch in withOptions ([1e3c4cb](https://github.com/parallel-web/parallel-sdk-typescript/commit/1e3c4cbb08404440b34dc10dc67beb273fa658f3))
+* **client:** get fetchOptions type more reliably ([c90df05](https://github.com/parallel-web/parallel-sdk-typescript/commit/c90df05c6ed462bffb1004861f33ed3f2ff4cdcc))
+* compat with more runtimes ([b197b9f](https://github.com/parallel-web/parallel-sdk-typescript/commit/b197b9ffc02621b1743ba5ff52b59b524aef94b1))
+* publish script — handle NPM errors correctly ([4c38358](https://github.com/parallel-web/parallel-sdk-typescript/commit/4c38358bade9566927ba83f123b06ea70d4c2140))
+
+
+### Chores
+
+* add docs to RequestOptions type ([6aae9a9](https://github.com/parallel-web/parallel-sdk-typescript/commit/6aae9a9b10709a8b53886f74e1a768f1b25e66d0))
+* adjust eslint.config.mjs ignore pattern ([1685764](https://github.com/parallel-web/parallel-sdk-typescript/commit/1685764727dd8d217dad3a24257dac5777839b44))
+* avoid type error in certain environments ([dc40584](https://github.com/parallel-web/parallel-sdk-typescript/commit/dc405849f7a23810049ea0409f9bdf4db785d149))
+* change publish docs url ([cd7500b](https://github.com/parallel-web/parallel-sdk-typescript/commit/cd7500b70c2ec7d1dc9534a582619042be13eb53))
+* **ci:** enable for pull requests ([2bd8a50](https://github.com/parallel-web/parallel-sdk-typescript/commit/2bd8a5000c5e442499ab1e7bd4186303e6e0539b))
+* **ci:** only run for pushes and fork pull requests ([07a77b8](https://github.com/parallel-web/parallel-sdk-typescript/commit/07a77b81a447dc1b0f195a44db309913cd6eebcf))
+* **client:** drop support for EOL node versions ([ae8f0d0](https://github.com/parallel-web/parallel-sdk-typescript/commit/ae8f0d0732f35ae967d1285cd3b6255f78822031))
+* **client:** improve path param validation ([c64babf](https://github.com/parallel-web/parallel-sdk-typescript/commit/c64babf6852c1f078b4151f1a9a9e047686dc21f))
+* **client:** refactor imports ([9ed8458](https://github.com/parallel-web/parallel-sdk-typescript/commit/9ed845857b38ff482b624eb171a4767965ce6562))
+* **deps:** bump eslint-plugin-prettier ([326f222](https://github.com/parallel-web/parallel-sdk-typescript/commit/326f222fd6f1f305370681a2aad70ee2776f62e1))
+* **docs:** grammar improvements ([9afb7db](https://github.com/parallel-web/parallel-sdk-typescript/commit/9afb7db5530567fc606fc2084172705b5cf3a627))
+* **docs:** use top-level-await in example snippets ([15c8ab4](https://github.com/parallel-web/parallel-sdk-typescript/commit/15c8ab49b9e9438ccbb3b066def8d2c4ef0f8748))
+* go live ([5e6dbbb](https://github.com/parallel-web/parallel-sdk-typescript/commit/5e6dbbb6f668125403e5261b6ce4bc80861eb627))
+* improve publish-npm script --latest tag logic ([9feb9eb](https://github.com/parallel-web/parallel-sdk-typescript/commit/9feb9eb2fb9ee5d993872a8b8bc4ce30180f8084))
+* **internal:** add pure annotations, make base APIResource abstract ([cf3a8d2](https://github.com/parallel-web/parallel-sdk-typescript/commit/cf3a8d27388f773c166c8283b0e89e9a16ae04e4))
+* **internal:** codegen related update ([7dfd25b](https://github.com/parallel-web/parallel-sdk-typescript/commit/7dfd25bb7ed0a0b7d6a40d74d137b7f2d34c332c))
+* **internal:** fix readablestream types in node 20 ([340f07e](https://github.com/parallel-web/parallel-sdk-typescript/commit/340f07e2c4f1db967f2f00c909b1283c5b0dbad3))
+* **internal:** refactor utils ([95c2945](https://github.com/parallel-web/parallel-sdk-typescript/commit/95c2945cef58d5d7b13c968c70de500148705720))
+* **internal:** share typescript helpers ([fa5b3f1](https://github.com/parallel-web/parallel-sdk-typescript/commit/fa5b3f1e6e9ad059adf73bc8475455247ec71df3))
+* **internal:** update jest config ([b740c53](https://github.com/parallel-web/parallel-sdk-typescript/commit/b740c5312f264b90fd8b8690a5770209486f2e1f))
+* make some internal functions async ([ae148ed](https://github.com/parallel-web/parallel-sdk-typescript/commit/ae148ede844821590bb04b6b5f07e7419660cda4))
+* **package:** remove engines ([8c81289](https://github.com/parallel-web/parallel-sdk-typescript/commit/8c812899a5099a2a5fae23d9863effef9ae66581))
+* **readme:** update badges ([10d3e6a](https://github.com/parallel-web/parallel-sdk-typescript/commit/10d3e6abff37d19ad633430f390f8d899d069bb3))
+* **readme:** use better example snippet for undocumented params ([6b403ec](https://github.com/parallel-web/parallel-sdk-typescript/commit/6b403ecc4ed7dc04bf80d719cd357b3bfd32d47e))
+* **ts:** reorder package.json imports ([4e6e456](https://github.com/parallel-web/parallel-sdk-typescript/commit/4e6e456893e96f2fb0d6cbbf4022193e4207f99e))
+
+
+### Documentation
+
+* **readme:** fix typo ([0c499fe](https://github.com/parallel-web/parallel-sdk-typescript/commit/0c499fe10500f3a667941cf8acbdf5d88cb67182))
+
+
+### Refactors
+
+* **types:** replace Record with mapped types ([ab87e9c](https://github.com/parallel-web/parallel-sdk-typescript/commit/ab87e9c8c2fdceaf24a3f310647bf0912c1c3dcb))
diff --git a/package.json b/package.json
index c05e27e..bf29524 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "parallel-web",
- "version": "0.0.1-alpha.0",
+ "version": "0.1.0",
"description": "The official TypeScript library for the Parallel API",
"author": "Parallel ",
"types": "dist/index.d.ts",
diff --git a/src/version.ts b/src/version.ts
index db692bc..1baa228 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -1 +1 @@
-export const VERSION = '0.0.1-alpha.0'; // x-release-please-version
+export const VERSION = '0.1.0'; // x-release-please-version