mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-10-12 19:25:21 +02:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3d1bcdd1b |
@@ -109,31 +109,9 @@ jobs:
|
||||
- name: Build
|
||||
run: yarn build
|
||||
|
||||
test-js:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
working-directory:
|
||||
- "libs/sdk-js"
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ matrix.working-directory }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Node.js (LTS)
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "yarn"
|
||||
cache-dependency-path: ${{ matrix.working-directory }}/yarn.lock
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
- name: Run tests
|
||||
run: yarn test
|
||||
|
||||
ci_success:
|
||||
name: "CI Success"
|
||||
needs: [lint, lint-js, test, test-langgraph, test-scheduler-kafka, integration-test, test-js]
|
||||
needs: [lint, lint-js, test, test-langgraph, test-scheduler-kafka, integration-test]
|
||||
if: |
|
||||
always()
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -279,58 +279,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/projects/{project_id}/revisions/{revision_id}/deploy": {
|
||||
"post": {
|
||||
"tags": ["Revisions (v1)"],
|
||||
"summary": "Deploy Revision",
|
||||
"description": "Deploy revision by ID.\n\nThis endpoint redeploys the deployment of a revision without rebuilding the image for the deployment. Redeploying the deployment of a revision may mitigate intermittent issues with a deployment.\n\nThe revision must be in the `DEPLOYED` status and must be the latest revision of the project.",
|
||||
"operationId": "deploy_revision_projects__project_id__revisions__revision_id__deploy_post",
|
||||
"parameters": [
|
||||
{
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Project ID"
|
||||
},
|
||||
"name": "project_id",
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Revision ID"
|
||||
},
|
||||
"name": "revision_id",
|
||||
"in": "path"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"400": {
|
||||
"description": "Revision is not in DEPLOYED status or revision is not the latest revision for the project.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Revision not found.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/projects/{project_id}/revisions/{revision_id}/interrupt": {
|
||||
"post": {
|
||||
"tags": ["Revisions (v1)"],
|
||||
@@ -371,6 +319,31 @@
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"EnvVar": {
|
||||
"type": "object",
|
||||
"description": "An environment variable or secret.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Environment variable or secret name.",
|
||||
"required": true
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"description": "Environment variable or secret value.",
|
||||
"required": true
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"default",
|
||||
"secret"
|
||||
],
|
||||
"description": "Field to designate type of the environment variable (default) or secret.",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ContainerSpec": {
|
||||
"type": "object",
|
||||
"description": "Container specification for a revision's deployment.\n\nIf any field is omitted or set to `null`, the internal default value is used depending on the deployment type (`dev` or `prod`).",
|
||||
@@ -431,42 +404,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"EnvVar": {
|
||||
"type": "object",
|
||||
"description": "An environment variable or secret.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Environment variable or secret name.",
|
||||
"required": true
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"description": "Environment variable or secret value.",
|
||||
"required": true
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"default",
|
||||
"secret"
|
||||
],
|
||||
"description": "Field to designate type of the environment variable (default) or secret.",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ErrorResponse": {
|
||||
"type": "object",
|
||||
"description": "Error response.",
|
||||
"properties": {
|
||||
"detail": {
|
||||
"type": "string",
|
||||
"description": "Error details.",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"Project": {
|
||||
"type": "object",
|
||||
"description": "A project corresponds to a LangGraph Server deployment and the associated LangSmith tracing project.",
|
||||
|
||||
@@ -289,7 +289,4 @@ RUN set -ex && \
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/__outer_graphs/src/agent.py:graph", "storm": "/deps/__outer_graphs/src/storm.py:graph"}'
|
||||
```
|
||||
|
||||
???+ note "Updating your langgraph.json file"
|
||||
The `langgraph dockerfile` command translates all the configuration in your `langgraph.json` file into Dockerfile commands. When using this command, you will have to re-run it whenever you update your `langgraph.json` file. Otherwise, your changes will not be reflected when you build or run the dockerfile.
|
||||
```
|
||||
@@ -62,9 +62,6 @@ The server includes all API endpoints for your graph's runs, threads, assistants
|
||||
|
||||
The `langgraph dockerfile` command generates a [Dockerfile](https://docs.docker.com/reference/dockerfile/) that can be used to build images for and deploy instances of the [LangGraph API server](./langgraph_server.md). This is useful if you want to further customize the dockerfile or deploy in a more custom way.
|
||||
|
||||
??? note "Updating your langgraph.json file"
|
||||
The `langgraph dockerfile` command translates all the configuration in your `langgraph.json` file into Dockerfile commands. When using this command, you will have to re-run it whenever you update your `langgraph.json` file. Otherwise, your changes will not be reflected when you build or run the dockerfile.
|
||||
|
||||
## Related
|
||||
|
||||
- [LangGraph CLI API Reference](../cloud/reference/cli.md)
|
||||
|
||||
@@ -29,7 +29,6 @@ You will eventually need to pass in the following environment variables to the L
|
||||
- `DATABASE_URI`: Postgres connection details. Postgres will be used to store assistants, threads, runs, persist thread state and long term memory, and to manage the state of the background task queue with 'exactly once' semantics.
|
||||
- `LANGSMITH_API_KEY`: (If using [Self-Hosted Lite](../concepts/deployment_options.md#self-hosted-lite)) LangSmith API key. This will be used to authenticate ONCE at server start up.
|
||||
- `LANGGRAPH_CLOUD_LICENSE_KEY`: (If using [Self-Hosted Enterprise](../concepts/deployment_options.md#self-hosted-enterprise)) LangGraph Platform license key. This will be used to authenticate ONCE at server start up.
|
||||
- `LANGCHAIN_ENDPOINT`: To send traces to a [self-hosted LangSmith](https://docs.smith.langchain.com/self_hosting) instance, set `LANGCHAIN_ENDPOINT` to the hostname of the self-hosted LangSmith instance.
|
||||
|
||||
|
||||
## Build the Docker Image
|
||||
|
||||
@@ -208,7 +208,7 @@
|
||||
"from typing import Optional\n",
|
||||
"\n",
|
||||
"from langchain.chat_models import init_chat_model\n",
|
||||
"from langgraph.prebuilt import InjectedStore\n",
|
||||
"from langchain_core.tools import InjectedToolArg\n",
|
||||
"from langgraph.store.base import BaseStore\n",
|
||||
"from typing_extensions import Annotated\n",
|
||||
"\n",
|
||||
@@ -232,7 +232,7 @@
|
||||
" content: str,\n",
|
||||
" *,\n",
|
||||
" memory_id: Optional[uuid.UUID] = None,\n",
|
||||
" store: Annotated[BaseStore, InjectedStore],\n",
|
||||
" store: Annotated[BaseStore, InjectedToolArg],\n",
|
||||
"):\n",
|
||||
" \"\"\"Upsert a memory in the database.\"\"\"\n",
|
||||
" # The LLM can use this tool to store a new memory\n",
|
||||
|
||||
@@ -246,7 +246,7 @@
|
||||
"\n",
|
||||
"Define the (`fetch_user_flight_information`) tool to let the agent see the current user's flight information. Then define tools to search for flights and manage the passenger's bookings stored in the SQL database.\n",
|
||||
"\n",
|
||||
"We then can [access the RunnableConfig](https://python.langchain.com/docs/how_to/tool_configure/#inferring-by-parameter-type) for a given run to check the `passenger_id` of the user accessing this application. The LLM never has to provide these explicitly, they are provided for a given invocation of the graph so that each user cannot access other passengers' booking information.\n",
|
||||
"We the can [access the RunnableConfig](https://python.langchain.com/docs/how_to/tool_configure/#inferring-by-parameter-type) for a given run to check the `passenger_id` of the user accessing this application. The LLM never has to provide these explicitly, they are provided for a given invocation of the graph so that each user cannot access other passengers' booking information.\n",
|
||||
"\n",
|
||||
"<div class=\"admonition warning\">\n",
|
||||
" <p class=\"admonition-title\">Compatibility</p>\n",
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
import asyncio
|
||||
import functools
|
||||
import weakref
|
||||
from typing import Any, Callable, Iterable, Literal, Optional, TypeVar, Union
|
||||
import sys
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Iterable,
|
||||
Literal,
|
||||
Optional,
|
||||
TypeVar,
|
||||
Union,
|
||||
AsyncIterator,
|
||||
)
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from langgraph.store.base import (
|
||||
BaseStore,
|
||||
@@ -17,9 +28,15 @@ from langgraph.store.base import (
|
||||
SearchOp,
|
||||
_validate_namespace,
|
||||
)
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
F = TypeVar("F", bound=Callable)
|
||||
|
||||
SUPPORTS_EXC_NOTES = sys.version_info >= (3, 11)
|
||||
|
||||
|
||||
def _check_loop(func: F) -> F:
|
||||
@functools.wraps(func)
|
||||
@@ -67,7 +84,8 @@ class AsyncBatchedBaseStore(BaseStore):
|
||||
) -> Optional[Item]:
|
||||
fut = self._loop.create_future()
|
||||
self._aqueue[fut] = GetOp(namespace, key)
|
||||
return await fut
|
||||
async with _raise_with_queue_stats(self._aqueue):
|
||||
return await fut
|
||||
|
||||
async def asearch(
|
||||
self,
|
||||
@@ -81,7 +99,8 @@ class AsyncBatchedBaseStore(BaseStore):
|
||||
) -> list[SearchItem]:
|
||||
fut = self._loop.create_future()
|
||||
self._aqueue[fut] = SearchOp(namespace_prefix, filter, limit, offset, query)
|
||||
return await fut
|
||||
async with _raise_with_queue_stats(self._aqueue):
|
||||
return await fut
|
||||
|
||||
async def aput(
|
||||
self,
|
||||
@@ -93,7 +112,8 @@ class AsyncBatchedBaseStore(BaseStore):
|
||||
_validate_namespace(namespace)
|
||||
fut = self._loop.create_future()
|
||||
self._aqueue[fut] = PutOp(namespace, key, value, index)
|
||||
return await fut
|
||||
async with _raise_with_queue_stats(self._aqueue):
|
||||
return await fut
|
||||
|
||||
async def adelete(
|
||||
self,
|
||||
@@ -102,7 +122,8 @@ class AsyncBatchedBaseStore(BaseStore):
|
||||
) -> None:
|
||||
fut = self._loop.create_future()
|
||||
self._aqueue[fut] = PutOp(namespace, key, None)
|
||||
return await fut
|
||||
async with _raise_with_queue_stats(self._aqueue):
|
||||
return await fut
|
||||
|
||||
async def alist_namespaces(
|
||||
self,
|
||||
@@ -127,7 +148,8 @@ class AsyncBatchedBaseStore(BaseStore):
|
||||
offset=offset,
|
||||
)
|
||||
self._aqueue[fut] = op
|
||||
return await fut
|
||||
async with _raise_with_queue_stats(self._aqueue):
|
||||
return await fut
|
||||
|
||||
@_check_loop
|
||||
def batch(self, ops: Iterable[Op]) -> list[Result]:
|
||||
@@ -281,3 +303,17 @@ async def _run(
|
||||
break
|
||||
# remove strong ref to store
|
||||
del s
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def _raise_with_queue_stats(
|
||||
queue: dict[asyncio.Future, Op]
|
||||
) -> AsyncIterator[None]:
|
||||
try:
|
||||
yield
|
||||
except asyncio.CancelledError as e:
|
||||
if SUPPORTS_EXC_NOTES:
|
||||
e.add_note(f"Queue size: {len(queue)}")
|
||||
else:
|
||||
logger.warning(f"Queue size: {len(queue)}")
|
||||
raise
|
||||
|
||||
@@ -100,7 +100,7 @@ class Config(TypedDict, total=False):
|
||||
def _parse_version(version_str: str) -> tuple[int, int]:
|
||||
"""Parse a version string into a tuple of (major, minor)."""
|
||||
try:
|
||||
major, minor = map(int, version_str.split("-")[0].split("."))
|
||||
major, minor = map(int, version_str.split("."))
|
||||
return (major, minor)
|
||||
except ValueError:
|
||||
raise click.UsageError(f"Invalid version format: {version_str}") from None
|
||||
@@ -159,7 +159,7 @@ def validate_config(config: Config) -> Config:
|
||||
if config.get("python_version"):
|
||||
pyversion = config["python_version"]
|
||||
if not pyversion.count(".") == 1 or not all(
|
||||
part.isdigit() for part in pyversion.split("-")[0].split(".")
|
||||
part.isdigit() for part in pyversion.split(".")
|
||||
):
|
||||
raise click.UsageError(
|
||||
f"Invalid Python version format: {pyversion}. "
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "langgraph-cli"
|
||||
version = "0.1.66"
|
||||
version = "0.1.65"
|
||||
description = "CLI for interacting with LangGraph API"
|
||||
authors = []
|
||||
license = "MIT"
|
||||
|
||||
@@ -91,12 +91,6 @@ def test_validate_config():
|
||||
validate_config({"python_version": "3.10"})
|
||||
assert "Minimum required version" in str(exc_info.value)
|
||||
|
||||
config = validate_config({"python_version": "3.11-bullseye", "dependencies": ["."], "graphs": {"agent": "./agent.py:graph"}})
|
||||
assert config["python_version"] == "3.11-bullseye"
|
||||
|
||||
config = validate_config({"python_version": "3.12-slim", "dependencies": ["."], "graphs": {"agent": "./agent.py:graph"}})
|
||||
assert config["python_version"] == "3.12-slim"
|
||||
|
||||
|
||||
def test_validate_config_file():
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/** @type {import('jest').Config} */
|
||||
export default {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
extensionsToTreatAsEsm: ['.ts'],
|
||||
moduleNameMapper: {
|
||||
'^(\\.{1,2}/.*)\\.js$': '$1',
|
||||
},
|
||||
transform: {
|
||||
'^.+\\.tsx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
useESM: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@langchain/langgraph-sdk",
|
||||
"version": "0.0.36",
|
||||
"version": "0.0.35",
|
||||
"description": "Client library for interacting with the LangGraph API",
|
||||
"type": "module",
|
||||
"packageManager": "yarn@1.22.19",
|
||||
@@ -9,8 +9,7 @@
|
||||
"build": "yarn clean && yarn lc_build --create-entrypoints --pre --tree-shaking",
|
||||
"prepublish": "yarn run build",
|
||||
"format": "prettier --write src",
|
||||
"lint": "prettier --check src && tsc --noEmit",
|
||||
"test": "NODE_OPTIONS=--experimental-vm-modules jest --testPathIgnorePatterns=\\.int\\.test.ts"
|
||||
"lint": "prettier --check src && tsc --noEmit"
|
||||
},
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
@@ -21,16 +20,12 @@
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "^29.7.0",
|
||||
"@langchain/scripts": "^0.1.4",
|
||||
"@tsconfig/recommended": "^1.0.2",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^20.12.12",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"concat-md": "^0.5.1",
|
||||
"jest": "^29.7.0",
|
||||
"prettier": "^3.2.5",
|
||||
"ts-jest": "^29.1.2",
|
||||
"typedoc": "^0.26.1",
|
||||
"typedoc-plugin-markdown": "^4.1.0",
|
||||
"typescript": "^5.4.5"
|
||||
|
||||
@@ -37,7 +37,7 @@ import {
|
||||
} from "./types.js";
|
||||
import { mergeSignals } from "./utils/signals.js";
|
||||
import { getEnvironmentVariable } from "./utils/env.js";
|
||||
import { _getFetchImplementation } from "./singletons/fetch.js";
|
||||
|
||||
/**
|
||||
* Get the API key from the environment.
|
||||
* Precedence:
|
||||
@@ -164,8 +164,7 @@ class BaseClient {
|
||||
signal?: AbortSignal;
|
||||
},
|
||||
): Promise<T> {
|
||||
const response = await this.asyncCaller.call(
|
||||
_getFetchImplementation(),
|
||||
const response = await this.asyncCaller.fetch(
|
||||
...this.prepareFetchOptions(path, options),
|
||||
);
|
||||
if (response.status === 202 || response.status === 204) {
|
||||
@@ -753,8 +752,7 @@ export class RunsClient extends BaseClient {
|
||||
|
||||
const endpoint =
|
||||
threadId == null ? `/runs/stream` : `/threads/${threadId}/runs/stream`;
|
||||
const response = await this.asyncCaller.call(
|
||||
_getFetchImplementation(),
|
||||
const response = await this.asyncCaller.fetch(
|
||||
...this.prepareFetchOptions(endpoint, {
|
||||
method: "POST",
|
||||
json,
|
||||
@@ -1046,8 +1044,7 @@ export class RunsClient extends BaseClient {
|
||||
? { signal: options }
|
||||
: options;
|
||||
|
||||
const response = await this.asyncCaller.call(
|
||||
_getFetchImplementation(),
|
||||
const response = await this.asyncCaller.fetch(
|
||||
...this.prepareFetchOptions(`/threads/${threadId}/runs/${runId}/stream`, {
|
||||
method: "GET",
|
||||
timeoutMs: null,
|
||||
|
||||
@@ -17,6 +17,5 @@ export type {
|
||||
Checkpoint,
|
||||
Interrupt,
|
||||
} from "./schema.js";
|
||||
export { overrideFetchImplementation } from "./singletons/fetch.js";
|
||||
|
||||
export type { OnConflictBehavior, Command } from "./types.js";
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
// Wrap the default fetch call due to issues with illegal invocations
|
||||
// in some environments:
|
||||
// https://stackoverflow.com/questions/69876859/why-does-bind-fix-failed-to-execute-fetch-on-window-illegal-invocation-err
|
||||
// @ts-expect-error Broad typing to support a range of fetch implementations
|
||||
const DEFAULT_FETCH_IMPLEMENTATION = (...args: any[]) => fetch(...args);
|
||||
|
||||
const LANGSMITH_FETCH_IMPLEMENTATION_KEY = Symbol.for(
|
||||
"lg:fetch_implementation",
|
||||
);
|
||||
|
||||
/**
|
||||
* Overrides the fetch implementation used for LangSmith calls.
|
||||
* You should use this if you need to use an implementation of fetch
|
||||
* other than the default global (e.g. for dealing with proxies).
|
||||
* @param fetch The new fetch function to use.
|
||||
*/
|
||||
export const overrideFetchImplementation = (fetch: (...args: any[]) => any) => {
|
||||
(globalThis as any)[LANGSMITH_FETCH_IMPLEMENTATION_KEY] = fetch;
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const _getFetchImplementation: () => (...args: any[]) => any = () => {
|
||||
return (
|
||||
(globalThis as any)[LANGSMITH_FETCH_IMPLEMENTATION_KEY] ??
|
||||
DEFAULT_FETCH_IMPLEMENTATION
|
||||
);
|
||||
};
|
||||
@@ -1,74 +0,0 @@
|
||||
/* eslint-disable no-process-env */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { jest } from "@jest/globals";
|
||||
import { Client } from "../client.js";
|
||||
import { overrideFetchImplementation } from "../singletons/fetch.js";
|
||||
|
||||
describe.each([[""], ["mocked"]])("Client uses %s fetch", (description) => {
|
||||
let globalFetchMock: jest.Mock;
|
||||
let overriddenFetch: jest.Mock;
|
||||
let expectedFetchMock: jest.Mock;
|
||||
let unexpectedFetchMock: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
globalFetchMock = jest.fn(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
json: () =>
|
||||
Promise.resolve({
|
||||
batch_ingest_config: {
|
||||
use_multipart_endpoint: true,
|
||||
},
|
||||
}),
|
||||
text: () => Promise.resolve(""),
|
||||
}),
|
||||
);
|
||||
overriddenFetch = jest.fn(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
json: () =>
|
||||
Promise.resolve({
|
||||
batch_ingest_config: {
|
||||
use_multipart_endpoint: true,
|
||||
},
|
||||
}),
|
||||
text: () => Promise.resolve(""),
|
||||
}),
|
||||
);
|
||||
expectedFetchMock =
|
||||
description === "mocked" ? overriddenFetch : globalFetchMock;
|
||||
unexpectedFetchMock =
|
||||
description === "mocked" ? globalFetchMock : overriddenFetch;
|
||||
|
||||
if (description === "mocked") {
|
||||
overrideFetchImplementation(overriddenFetch);
|
||||
} else {
|
||||
overrideFetchImplementation(globalFetchMock);
|
||||
}
|
||||
// Mock global fetch
|
||||
(globalThis as any).fetch = globalFetchMock;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe("createRuns", () => {
|
||||
it("should create an example with the given input and generation", async () => {
|
||||
const client = new Client({ apiKey: "test-api-key" });
|
||||
|
||||
const thread = await client.threads.create();
|
||||
expect(expectedFetchMock).toHaveBeenCalledTimes(1);
|
||||
expect(unexpectedFetchMock).not.toHaveBeenCalled();
|
||||
|
||||
jest.clearAllMocks(); // Clear all mocks before the next operation
|
||||
|
||||
// Then clear & run the function
|
||||
await client.runs.create(thread.thread_id, "somegraph", {
|
||||
input: { foo: "bar" },
|
||||
});
|
||||
expect(expectedFetchMock).toHaveBeenCalledTimes(1);
|
||||
expect(unexpectedFetchMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
+7
-1880
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user