mirror of
https://github.com/langchain-ai/langgraphjs-api.git
synced 2026-07-19 21:33:46 -04:00
feat: pagination and tracing
This commit is contained in:
@@ -1,19 +1,19 @@
|
||||
import { Hono } from "hono";
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import { Hono } from "hono";
|
||||
|
||||
import { v4 as uuid } from "uuid";
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
getAssistantId,
|
||||
getGraph,
|
||||
getCachedStaticGraphSchema,
|
||||
getGraph,
|
||||
} from "../graph/load.mjs";
|
||||
import { getRuntimeGraphSchema } from "../graph/parser/index.mjs";
|
||||
|
||||
import { Assistants } from "../storage/ops.mjs";
|
||||
import * as schemas from "../schemas.mjs";
|
||||
import { HTTPException } from "hono/http-exception";
|
||||
import * as schemas from "../schemas.mjs";
|
||||
import { Assistants } from "../storage/ops.mjs";
|
||||
const api = new Hono();
|
||||
|
||||
const RunnableConfigSchema = z.object({
|
||||
@@ -71,6 +71,7 @@ api.post(
|
||||
const payload = c.req.valid("json");
|
||||
const result: unknown[] = [];
|
||||
|
||||
let total = 0;
|
||||
for await (const item of Assistants.search(
|
||||
{
|
||||
graph_id: payload.graph_id,
|
||||
@@ -80,9 +81,13 @@ api.post(
|
||||
},
|
||||
c.var.auth,
|
||||
)) {
|
||||
result.push(item);
|
||||
result.push(item.assistant);
|
||||
if (total === 0) {
|
||||
total = item.total;
|
||||
}
|
||||
}
|
||||
|
||||
c.res.headers.set("X-Pagination-Total", total.toString());
|
||||
return c.json(result);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -2,7 +2,32 @@ import { Hono } from "hono";
|
||||
|
||||
const api = new Hono();
|
||||
|
||||
api.get("/info", (c) => c.json({ flags: { assistants: true, crons: false } }));
|
||||
// read env variable
|
||||
const env = process.env;
|
||||
|
||||
api.get("/info", (c) => {
|
||||
const langsmithApiKey = env["LANGSMITH_API_KEY"] || env["LANGCHAIN_API_KEY"];
|
||||
|
||||
const langsmithTracing = (() => {
|
||||
if (langsmithApiKey) {
|
||||
// Check if any tracing variable is explicitly set to "false"
|
||||
const tracingVars = [
|
||||
env["LANGCHAIN_TRACING_V2"],
|
||||
env["LANGCHAIN_TRACING"],
|
||||
env["LANGSMITH_TRACING_V2"],
|
||||
env["LANGSMITH_TRACING"],
|
||||
];
|
||||
|
||||
// Return true unless explicitly disabled
|
||||
return !tracingVars.some((val) => val === "false" || val === "False");
|
||||
}
|
||||
return undefined;
|
||||
})();
|
||||
return c.json({
|
||||
flags: { assistants: true, crons: false, langsmith: langsmithTracing },
|
||||
env,
|
||||
});
|
||||
});
|
||||
|
||||
api.get("/ok", (c) => c.json({ ok: true }));
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export const cors = (
|
||||
if (cors == null) {
|
||||
return honoCors({
|
||||
origin: "*",
|
||||
exposeHeaders: ["content-location"],
|
||||
exposeHeaders: ["content-location", "x-pagination-total"],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -33,16 +33,21 @@ export const cors = (
|
||||
}
|
||||
: (cors.allow_origins ?? []);
|
||||
|
||||
if (
|
||||
!!cors.expose_headers?.length &&
|
||||
cors.expose_headers.some(
|
||||
(i) => i.toLocaleLowerCase() === "content-location",
|
||||
)
|
||||
) {
|
||||
console.warn(
|
||||
"Adding missing `Content-Location` header in `cors.expose_headers`.",
|
||||
);
|
||||
cors.expose_headers.push("content-location");
|
||||
if (cors.expose_headers?.length) {
|
||||
const headersSet = new Set(cors.expose_headers.map((h) => h.toLowerCase()));
|
||||
|
||||
if (!headersSet.has("content-location")) {
|
||||
console.warn(
|
||||
"Adding missing `Content-Location` header in `cors.expose_headers`.",
|
||||
);
|
||||
cors.expose_headers.push("content-location");
|
||||
}
|
||||
if (!headersSet.has("x-pagination-total")) {
|
||||
console.warn(
|
||||
"Adding missing `X-Pagination-Total` header in `cors.expose_headers`.",
|
||||
);
|
||||
cors.expose_headers.push("x-pagination-total");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: handle `cors.allow_credentials`
|
||||
|
||||
@@ -323,7 +323,7 @@ export class Assistants {
|
||||
offset: number;
|
||||
},
|
||||
auth: AuthContext | undefined,
|
||||
) {
|
||||
): AsyncGenerator<{ assistant: Assistant; total: number }> {
|
||||
const [filters] = await handleAuthEvent(auth, "assistants:search", {
|
||||
graph_id: options.graph_id,
|
||||
metadata: options.metadata,
|
||||
@@ -360,11 +360,20 @@ export class Assistants {
|
||||
return bCreatedAt - aCreatedAt;
|
||||
});
|
||||
|
||||
// Calculate total count before pagination
|
||||
const total = filtered.length;
|
||||
|
||||
for (const assistant of filtered.slice(
|
||||
options.offset,
|
||||
options.offset + options.limit,
|
||||
)) {
|
||||
yield { ...assistant, name: assistant.name ?? assistant.graph_id };
|
||||
yield {
|
||||
assistant: {
|
||||
...assistant,
|
||||
name: assistant.name ?? assistant.graph_id,
|
||||
},
|
||||
total,
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user