Skip to content

Commit 3168d47

Browse files
[Feat] Update Typescript SDK & Documentation (#877)
This pull request updates the TypeScript SDK to version 0.6.1, introducing new features such as tool usage for LLMs and image generation configurations, and improving documentation generation. It also includes several important fixes, such as correctly serializing and URI-encoding array query parameters across multiple API clients, which enhances correctness and security. The Zod schemas have been significantly updated to reflect the new features and improve validation.
1 parent bcd26ff commit 3168d47

File tree

16 files changed

+262
-224
lines changed

16 files changed

+262
-224
lines changed

clients/typescript/__tests__/gentable.test.ts

Lines changed: 5 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,8 @@ describe("APIClient Gentable", () => {
5858

5959
// Store all embedding models
6060
const allEmbeddingModels = models.data.filter((model) => model.capabilities.includes("embed"));
61-
embeddingModels = allEmbeddingModels.length > 0
62-
? allEmbeddingModels.map((model) => model.id)
63-
: ["ellm/sentence-transformers/all-MiniLM-L6-v2"];
61+
embeddingModels =
62+
allEmbeddingModels.length > 0 ? allEmbeddingModels.map((model) => model.id) : ["ellm/sentence-transformers/all-MiniLM-L6-v2"];
6463

6564
// Set the first one as default
6665
embeddingModel = embeddingModels[0]!;
@@ -466,7 +465,7 @@ describe("APIClient Gentable", () => {
466465
}
467466
});
468467

469-
it("add row to action table with reindex", async () => {
468+
it("add row to action table - no stream", async () => {
470469
for await (const { id: table_id } of _getTable("action")) {
471470
const response = await client.table.addRow({
472471
table_type: "action",
@@ -487,59 +486,7 @@ describe("APIClient Gentable", () => {
487486
}
488487
});
489488

490-
it("add row to action table without reindex", async () => {
491-
for await (const { id: table_id } of _getTable("action")) {
492-
const response = await client.table.addRow({
493-
table_type: "action",
494-
data: [
495-
{
496-
question: "What is penguin?"
497-
},
498-
{
499-
question: "What is help?"
500-
}
501-
],
502-
table_id: table_id,
503-
concurrent: true
504-
});
505-
506-
const parsedData = MultiRowCompletionResponseSchema.parse(response);
507-
expect(parsedData).toEqual(response);
508-
}
509-
});
510-
511-
it("add row to action table - stream with reindex", async () => {
512-
for await (const { id: table_id } of _getTable("action")) {
513-
const response = await client.table.addRowStream({
514-
table_type: "action",
515-
data: [
516-
{
517-
question: "What is penguin?"
518-
},
519-
{
520-
question: "What is help?"
521-
}
522-
],
523-
table_id: table_id,
524-
concurrent: true
525-
});
526-
527-
expect(response).toBeInstanceOf(ReadableStream);
528-
const reader = response.getReader();
529-
let chunk_count: number = 0;
530-
while (true) {
531-
const { done, value } = await reader.read();
532-
if (done) {
533-
break;
534-
}
535-
// console.log(value) ;
536-
chunk_count += 1;
537-
}
538-
expect(chunk_count).toBeGreaterThan(2);
539-
}
540-
});
541-
542-
it("add row to action table - stream without reindex", async () => {
489+
it("add row to action table - stream", async () => {
543490
for await (const { id: table_id } of _getTable("action")) {
544491
const response = await client.table.addRowStream({
545492
table_type: "action",
@@ -1053,7 +1000,6 @@ describe("APIClient Gentable", () => {
10531000
column_ids: table.cols.length && table.cols[3]?.id ? [table.cols[3].id] : undefined
10541001
});
10551002

1056-
10571003
const parsedData = GetConversationThreadResponseSchema.parse(response);
10581004
expect(parsedData).toEqual(response);
10591005
}
@@ -1147,7 +1093,7 @@ describe("APIClient Gentable", () => {
11471093
}
11481094
}
11491095
});
1150-
1096+
11511097
const parsedData = TableMetaResponseSchema.parse(response);
11521098
expect(response).toEqual(parsedData);
11531099
}
@@ -1438,7 +1384,6 @@ describe("APIClient Gentable", () => {
14381384
table_id: table_id
14391385
});
14401386

1441-
14421387
expect(responseAddRow.rows.length).toBe(responseListRows.items.length);
14431388

14441389
const exportTableDataResponse = await client.table.exportTableData({

clients/typescript/__tests__/prices.test.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ describe("APIClient Prices", () => {
7373
included: { unit_cost: 0, up_to: 0.75 },
7474
tiers: [],
7575
unit: "GiB"
76+
},
77+
image_tokens: {
78+
name: "Image tokens",
79+
included: { unit_cost: 0, up_to: 0.75 },
80+
tiers: [],
81+
unit: "Million Tokens"
7682
}
7783
}
7884
});
@@ -138,6 +144,12 @@ describe("APIClient Prices", () => {
138144
included: { unit_cost: 0, up_to: 0.75 },
139145
tiers: [],
140146
unit: "GiB"
147+
},
148+
image_tokens: {
149+
name: "Image tokens",
150+
included: { unit_cost: 0, up_to: 0.75 },
151+
tiers: [],
152+
unit: "Million Tokens"
141153
}
142154
}
143155
});
@@ -164,7 +176,6 @@ describe("APIClient Prices", () => {
164176
if (response.id !== undefined) {
165177
await client.prices.deletePricePlan(response.id);
166178
}
167-
168179
});
169180

170181
it("list price plans - without limit and offset", async () => {
@@ -283,6 +294,12 @@ describe("APIClient Prices", () => {
283294
included: { unit_cost: 0, up_to: 0.75 },
284295
tiers: [],
285296
unit: "GiB"
297+
},
298+
image_tokens: {
299+
name: "Image tokens",
300+
included: { unit_cost: 0, up_to: 0.75 },
301+
tiers: [],
302+
unit: "Million Tokens"
286303
}
287304
}
288305
});

clients/typescript/__tests__/tasks.test.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import JamAI, { PROGRESS_STATES } from "@/index";
1+
import JamAI from "@/index";
22
import { afterAll, beforeAll, describe, expect, it, jest } from "@jest/globals";
33
import dotenv from "dotenv";
44
import { cleanupTestEnvironment, setupTestEnvironment, TestContext } from "./testUtils";
5+
import { PROGRESS_STATES } from "@/resources/tasks/types";
56

67
dotenv.config({
78
path: "__tests__/.env"
@@ -19,8 +20,6 @@ describe("APIClient Tasks", () => {
1920
beforeAll(async () => {
2021
testContext = await setupTestEnvironment();
2122
client = testContext.client;
22-
23-
2423
});
2524

2625
afterAll(async function () {
@@ -90,8 +89,6 @@ describe("APIClient Tasks", () => {
9089
expect(response).toBeNull();
9190
});
9291

93-
94-
9592
it("poll progress with different initial wait times", async () => {
9693
const testKey = "test-initial-wait-key";
9794

clients/typescript/__tests__/testUtils.ts

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@ export interface TestContext {
2323
* Also cleans up existing tables for gentable tests
2424
* Optionally creates model configs and deployments
2525
*/
26-
export async function setupTestEnvironment(options?: {
27-
cleanupTables?: boolean;
28-
createModels?: boolean;
29-
}): Promise<TestContext> {
26+
export async function setupTestEnvironment(options?: { cleanupTables?: boolean; createModels?: boolean }): Promise<TestContext> {
3027
// Default cleanupTables to true if not provided
3128
const { cleanupTables = true, createModels = false } = options ?? {};
3229
const myuuid = uuidv4();
@@ -50,9 +47,8 @@ export async function setupTestEnvironment(options?: {
5047

5148
// Check for existing user with id="0"
5249
const users = await setupClient.users.listUsers();
53-
const existingUser = users.items.find(u => u.id === "0");
54-
userId = "0"
55-
50+
const existingUser = users.items.find((u) => u.id === "0");
51+
userId = "0";
5652

5753
if (existingUser) {
5854
shouldDeleteUser = false;
@@ -70,7 +66,7 @@ export async function setupTestEnvironment(options?: {
7066

7167
// Check for existing organization with id="0"
7268
const orgs = await setupClient.organizations.listOrganizations();
73-
const existingOrg = orgs.items.find(o => o.id === "0");
69+
const existingOrg = orgs.items.find((o) => o.id === "0");
7470

7571
if (existingOrg) {
7672
organizationId = existingOrg.id;
@@ -151,10 +147,10 @@ export async function setupTestEnvironment(options?: {
151147
let modelConfigs: TestContext["modelConfigs"] | undefined;
152148
if (createModels) {
153149
const completionModelId = `openai/gpt-4o-mini-test-${uuidv4().substring(0, 8)}`;
154-
const completionRoutingId = 'gpt-4o-mini'
155-
const embeddingDimention = 1536
150+
const completionRoutingId = "gpt-4o-mini";
151+
const embeddingDimention = 1536;
156152
const embeddingModelId = `openai/text-embedding-test-${uuidv4().substring(0, 8)}`;
157-
const embeddingRoutingId = "text-embedding-3-small"
153+
const embeddingRoutingId = "text-embedding-3-small";
158154

159155
// Create completion model config (similar to GPT_4O_MINI_CONFIG)
160156
await client.models.createModelConfig({
@@ -224,15 +220,7 @@ export async function setupTestEnvironment(options?: {
224220
* Only deletes resources that were manually created (based on shouldDelete flags)
225221
*/
226222
export async function cleanupTestEnvironment(context: TestContext): Promise<void> {
227-
const {
228-
projectId,
229-
organizationId,
230-
userId,
231-
shouldDeleteProject,
232-
shouldDeleteOrganization,
233-
shouldDeleteUser,
234-
modelConfigs
235-
} = context;
223+
const { projectId, organizationId, userId, shouldDeleteProject, shouldDeleteOrganization, shouldDeleteUser, modelConfigs } = context;
236224

237225
// Create cleanup client
238226
const cleanupClient = new JamAI({

clients/typescript/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jamaibase",
3-
"version": "0.5.0",
3+
"version": "0.6.1",
44
"description": "JamAIBase Client SDK (JS/TS). JamAI Base: Let Your Database Orchestrate LLMs and RAG",
55
"main": "dist/index.cjs",
66
"module": "dist/index.mjs",
@@ -13,7 +13,7 @@
1313
"build": "/bin/bash build",
1414
"openapi-to-zod": "openapi-zod-client openapi.json -o zodschema/zodmodels.ts",
1515
"doc-ts-moduler": "typedoc --includeVersion --tsconfig tsconfig.build.json --includes ./dist/*.d.ts --includes ./dist/**/*.d.ts --includes ./dist/resources/**/*.d.ts --out docs-autogen-ts",
16-
"doc-ts": "typedoc --readme ./README.md --includeVersion --tsconfig tsconfig.build.json --entryPointStrategy expand --entryPoints ./dist/index.d.ts --entryPoints ./dist/resources --out docs-autogen-ts --categorizeByGroup --sort alphabetical && cp JamAI_Base_Cover.png docs-autogen-ts/"
16+
"doc-ts": "typedoc --readme ./README.md --includeVersion --tsconfig tsconfig.build.json --entryPointStrategy expand --entryPoints ./dist/index.d.ts --entryPoints ./dist/resources --exclude '**/shared/**' --out docs-autogen-ts --categorizeByGroup --sort alphabetical && cp JamAI_Base_Cover.png docs-autogen-ts/"
1717
},
1818
"repository": {
1919
"type": "git",

clients/typescript/src/helpers/utils.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,26 @@ export function createPaginationSchema<T>(itemSchema: z.ZodType<T>) {
4646
})
4747
);
4848
}
49+
50+
/**
51+
* Serializes query parameters for axios requests, properly handling arrays and skipping null/undefined values.
52+
* This prevents null/undefined values from being sent as literal strings (e.g., "columns=null").
53+
*
54+
* @param params - The query parameters object to serialize
55+
* @returns URL-encoded query string
56+
*
57+
* @example
58+
* serializeParams({ foo: 'bar', list: [1, 2], skip: null })
59+
* // Returns: "foo=bar&list=1&list=2"
60+
*/
61+
export function serializeParams(params: Record<string, any>): string {
62+
return Object.entries(params)
63+
.flatMap(([key, value]) =>
64+
Array.isArray(value)
65+
? value.map((val) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`)
66+
: value !== undefined && value !== null
67+
? `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
68+
: []
69+
)
70+
.join("&");
71+
}

clients/typescript/src/index.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,12 @@ class JamAI extends Base {
6161
this.file = new Files(sharedConfig);
6262
}
6363

64-
public override setApiKey(token: string){
65-
super.setApiKey(token)
64+
public override setApiKey(token: string) {
65+
super.setApiKey(token);
6666
}
6767

68-
public override setProjId(projectId: string){
69-
super.setProjId(projectId)
68+
public override setProjId(projectId: string) {
69+
super.setProjId(projectId);
7070
}
7171

7272
/**
@@ -94,27 +94,23 @@ class JamAI extends Base {
9494
* @property {Number} [socketActiveTTL=null] - Sets the time to live for active sockets, even if in use. If not set, sockets are released only when free. Default is null.
9595
*/
9696
public override setHttpsagentConfig(payload: Agent.HttpsOptions) {
97-
super.setHttpsagentConfig(payload)
97+
super.setHttpsagentConfig(payload);
9898
}
9999

100100
public override setAuthHeader(header: string) {
101-
super.setAuthHeader(header)
101+
super.setAuthHeader(header);
102102
}
103103

104104
public override async health(): Promise<AxiosResponse> {
105-
return await super.health()
105+
return await super.health();
106106
}
107107
}
108108

109-
// Re-export useful constants and types
110-
export { PROGRESS_STATES } from "@/resources/tasks/types";
111-
export type { ProgressState, ProgressResponse, PollProgressParams } from "@/resources/tasks/types";
112-
113109
// // Re-export types from internal modules for easier access
114110
// export * from "@/resources/base";
115111
// export * from "@/resources/files";
116112
// export * from "@/resources/gen_tables/tables";
117113
// export * from "@/resources/llm/chat";
118114
// export * from "@/resources/templates";
119115

120-
export default JamAI;
116+
export default JamAI;

clients/typescript/src/resources/base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export abstract class Base {
3636
protected maxRetries: number;
3737
protected httpClient: AxiosInstance;
3838
protected timeout: number | undefined;
39-
private sdkVersion = "0.3";
39+
private sdkVersion = "0.6.1";
4040

4141
/**
4242
* Creates an instance of APIClient.

0 commit comments

Comments
 (0)