lemma-sdk 0.2.20 → 0.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -1
- package/dist/auth.d.ts +42 -1
- package/dist/auth.js +43 -0
- package/dist/browser/lemma-client.js +49 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/namespaces/assistants.d.ts +2 -0
- package/dist/namespaces/assistants.js +3 -0
- package/dist/openapi_client/index.d.ts +2 -0
- package/dist/openapi_client/models/AvailableModelInfo.d.ts +8 -0
- package/dist/openapi_client/models/AvailableModelInfo.js +1 -0
- package/dist/openapi_client/models/AvailableModels.d.ts +3 -4
- package/dist/openapi_client/models/AvailableModels.js +2 -3
- package/dist/openapi_client/models/AvailableModelsListResponse.d.ts +7 -0
- package/dist/openapi_client/models/AvailableModelsListResponse.js +1 -0
- package/dist/openapi_client/models/FunctionRunResponse.d.ts +2 -0
- package/dist/openapi_client/services/ConversationsService.d.ts +8 -0
- package/dist/openapi_client/services/ConversationsService.js +12 -0
- package/dist/react/components/AssistantExperience.js +10 -2
- package/dist/react/components/assistant-types.d.ts +2 -0
- package/dist/react/useAssistantController.d.ts +2 -1
- package/dist/react/useAssistantController.js +34 -1
- package/dist/react/useAssistantRuntime.js +25 -4
- package/dist/react/useAssistantSession.js +14 -5
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,7 +108,12 @@ const assistantPayload: CreateAssistantInput = {
|
|
|
108
108
|
## Auth Helpers
|
|
109
109
|
|
|
110
110
|
```ts
|
|
111
|
-
import {
|
|
111
|
+
import {
|
|
112
|
+
LemmaClient,
|
|
113
|
+
buildAuthUrl,
|
|
114
|
+
buildFederatedLogoutUrl,
|
|
115
|
+
resolveSafeRedirectUri,
|
|
116
|
+
} from "lemma-sdk";
|
|
112
117
|
|
|
113
118
|
const client = new LemmaClient({
|
|
114
119
|
apiUrl: "https://api-next.asur.work",
|
|
@@ -131,6 +136,14 @@ await client.auth.signOut();
|
|
|
131
136
|
const token = await client.auth.getAccessToken();
|
|
132
137
|
const refreshed = await client.auth.refreshAccessToken();
|
|
133
138
|
client.auth.redirectToAuth({ mode: "signup", redirectUri: safeRedirect });
|
|
139
|
+
|
|
140
|
+
// Build upstream logout URL (server/client)
|
|
141
|
+
const federatedLogoutUrl = buildFederatedLogoutUrl(client.authUrl, {
|
|
142
|
+
redirectUri: safeRedirect,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Browser: sign out locally, then clear upstream SSO and return to app
|
|
146
|
+
await client.auth.redirectToFederatedLogout({ redirectUri: safeRedirect });
|
|
134
147
|
```
|
|
135
148
|
|
|
136
149
|
### Browser Testing With Injected Token
|
package/dist/auth.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export interface AuthState {
|
|
|
28
28
|
}
|
|
29
29
|
export type AuthListener = (state: AuthState) => void;
|
|
30
30
|
export type AuthRedirectMode = "login" | "signup";
|
|
31
|
+
type AuthQueryParams = Record<string, string | number | boolean | Array<string | number | boolean> | null | undefined>;
|
|
31
32
|
export interface BuildAuthUrlOptions {
|
|
32
33
|
/** Optional auth path segment relative to authUrl pathname, e.g. "callback" -> /auth/callback. */
|
|
33
34
|
path?: string;
|
|
@@ -36,7 +37,35 @@ export interface BuildAuthUrlOptions {
|
|
|
36
37
|
/** Redirect URI passed to auth service. */
|
|
37
38
|
redirectUri?: string;
|
|
38
39
|
/** Additional query parameters appended to auth URL. */
|
|
39
|
-
params?:
|
|
40
|
+
params?: AuthQueryParams;
|
|
41
|
+
}
|
|
42
|
+
export interface BuildFederatedLogoutUrlOptions {
|
|
43
|
+
/**
|
|
44
|
+
* Optional auth path segment for logout, relative to authUrl pathname.
|
|
45
|
+
* Defaults to "logout" (for example: https://auth.example.com/auth/logout).
|
|
46
|
+
*/
|
|
47
|
+
path?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Post-logout redirect URI passed to the auth service.
|
|
50
|
+
*/
|
|
51
|
+
redirectUri?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Query parameter name used for redirect URI. Defaults to "redirect_uri".
|
|
54
|
+
*/
|
|
55
|
+
redirectParam?: string;
|
|
56
|
+
/** Additional query parameters appended to logout URL. */
|
|
57
|
+
params?: AuthQueryParams;
|
|
58
|
+
}
|
|
59
|
+
export interface RedirectToFederatedLogoutOptions extends Omit<BuildFederatedLogoutUrlOptions, "redirectUri"> {
|
|
60
|
+
/**
|
|
61
|
+
* Post-logout redirect URI. Defaults to current location.
|
|
62
|
+
*/
|
|
63
|
+
redirectUri?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Whether to clear the local session before redirecting upstream.
|
|
66
|
+
* Defaults to true.
|
|
67
|
+
*/
|
|
68
|
+
localSignOut?: boolean;
|
|
40
69
|
}
|
|
41
70
|
export interface ResolveSafeRedirectUriOptions {
|
|
42
71
|
/** Origin for resolving relative paths. */
|
|
@@ -50,6 +79,7 @@ export declare function setTestingToken(token: string): void;
|
|
|
50
79
|
export declare function getTestingToken(): string | null;
|
|
51
80
|
export declare function clearTestingToken(): void;
|
|
52
81
|
export declare function buildAuthUrl(authUrl: string, options?: BuildAuthUrlOptions): string;
|
|
82
|
+
export declare function buildFederatedLogoutUrl(authUrl: string, options?: BuildFederatedLogoutUrlOptions): string;
|
|
53
83
|
export declare function resolveSafeRedirectUri(rawValue: string | null | undefined, options: ResolveSafeRedirectUriOptions): string;
|
|
54
84
|
export declare class AuthManager {
|
|
55
85
|
private readonly apiUrl;
|
|
@@ -112,6 +142,10 @@ export declare class AuthManager {
|
|
|
112
142
|
* Build auth URL for login/signup/custom auth sub-path.
|
|
113
143
|
*/
|
|
114
144
|
getAuthUrl(options?: BuildAuthUrlOptions): string;
|
|
145
|
+
/**
|
|
146
|
+
* Build upstream/federated logout URL.
|
|
147
|
+
*/
|
|
148
|
+
getFederatedLogoutUrl(options?: BuildFederatedLogoutUrlOptions): string;
|
|
115
149
|
/**
|
|
116
150
|
* Redirect to the auth service, passing the current URL as redirect_uri.
|
|
117
151
|
* After the user authenticates, the auth service should redirect back to
|
|
@@ -120,4 +154,11 @@ export declare class AuthManager {
|
|
|
120
154
|
redirectToAuth(options?: Omit<BuildAuthUrlOptions, "redirectUri"> & {
|
|
121
155
|
redirectUri?: string;
|
|
122
156
|
}): void;
|
|
157
|
+
/**
|
|
158
|
+
* Optional full logout flow:
|
|
159
|
+
* 1. clear local SDK/session cookies
|
|
160
|
+
* 2. redirect to auth service logout endpoint to terminate upstream SSO
|
|
161
|
+
*/
|
|
162
|
+
redirectToFederatedLogout(options?: RedirectToFederatedLogoutOptions): Promise<void>;
|
|
123
163
|
}
|
|
164
|
+
export {};
|
package/dist/auth.js
CHANGED
|
@@ -121,6 +121,26 @@ export function buildAuthUrl(authUrl, options = {}) {
|
|
|
121
121
|
}
|
|
122
122
|
return url.toString();
|
|
123
123
|
}
|
|
124
|
+
export function buildFederatedLogoutUrl(authUrl, options = {}) {
|
|
125
|
+
const url = new URL(authUrl);
|
|
126
|
+
url.pathname = resolveAuthPath(url.pathname, options.path ?? "logout");
|
|
127
|
+
for (const [key, value] of Object.entries(options.params ?? {})) {
|
|
128
|
+
if (value === null || value === undefined)
|
|
129
|
+
continue;
|
|
130
|
+
if (Array.isArray(value)) {
|
|
131
|
+
url.searchParams.delete(key);
|
|
132
|
+
for (const item of value) {
|
|
133
|
+
url.searchParams.append(key, String(item));
|
|
134
|
+
}
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
url.searchParams.set(key, String(value));
|
|
138
|
+
}
|
|
139
|
+
if (options.redirectUri && options.redirectUri.trim()) {
|
|
140
|
+
url.searchParams.set(options.redirectParam ?? "redirect_uri", options.redirectUri);
|
|
141
|
+
}
|
|
142
|
+
return url.toString();
|
|
143
|
+
}
|
|
124
144
|
export function resolveSafeRedirectUri(rawValue, options) {
|
|
125
145
|
const siteOrigin = normalizeOrigin(options.siteOrigin);
|
|
126
146
|
const blockedPaths = options.blockedPaths ?? DEFAULT_BLOCKED_REDIRECT_PATHS;
|
|
@@ -368,6 +388,12 @@ export class AuthManager {
|
|
|
368
388
|
getAuthUrl(options = {}) {
|
|
369
389
|
return buildAuthUrl(this.authUrl, options);
|
|
370
390
|
}
|
|
391
|
+
/**
|
|
392
|
+
* Build upstream/federated logout URL.
|
|
393
|
+
*/
|
|
394
|
+
getFederatedLogoutUrl(options = {}) {
|
|
395
|
+
return buildFederatedLogoutUrl(this.authUrl, options);
|
|
396
|
+
}
|
|
371
397
|
/**
|
|
372
398
|
* Redirect to the auth service, passing the current URL as redirect_uri.
|
|
373
399
|
* After the user authenticates, the auth service should redirect back to
|
|
@@ -380,4 +406,21 @@ export class AuthManager {
|
|
|
380
406
|
const redirectUri = options.redirectUri ?? window.location.href;
|
|
381
407
|
window.location.href = this.getAuthUrl({ ...options, redirectUri });
|
|
382
408
|
}
|
|
409
|
+
/**
|
|
410
|
+
* Optional full logout flow:
|
|
411
|
+
* 1. clear local SDK/session cookies
|
|
412
|
+
* 2. redirect to auth service logout endpoint to terminate upstream SSO
|
|
413
|
+
*/
|
|
414
|
+
async redirectToFederatedLogout(options = {}) {
|
|
415
|
+
this.assertBrowserContext();
|
|
416
|
+
const redirectUri = options.redirectUri ?? window.location.href;
|
|
417
|
+
const localSignOut = options.localSignOut ?? true;
|
|
418
|
+
if (localSignOut) {
|
|
419
|
+
await this.signOut();
|
|
420
|
+
}
|
|
421
|
+
window.location.href = this.getFederatedLogoutUrl({
|
|
422
|
+
...options,
|
|
423
|
+
redirectUri,
|
|
424
|
+
});
|
|
425
|
+
}
|
|
383
426
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"./browser.js": function (module, exports, require) {
|
|
4
4
|
"use strict";
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.ApiError = exports.setTestingToken = exports.resolveSafeRedirectUri = exports.getTestingToken = exports.clearTestingToken = exports.buildAuthUrl = exports.AuthManager = exports.LemmaClient = void 0;
|
|
6
|
+
exports.ApiError = exports.setTestingToken = exports.resolveSafeRedirectUri = exports.getTestingToken = exports.clearTestingToken = exports.buildFederatedLogoutUrl = exports.buildAuthUrl = exports.AuthManager = exports.LemmaClient = void 0;
|
|
7
7
|
/**
|
|
8
8
|
* Browser bundle entry point.
|
|
9
9
|
* Exposes LemmaClient as globalThis.LemmaClient.LemmaClient
|
|
@@ -19,6 +19,7 @@ Object.defineProperty(exports, "LemmaClient", { enumerable: true, get: function
|
|
|
19
19
|
var auth_js_1 = require("./auth.js");
|
|
20
20
|
Object.defineProperty(exports, "AuthManager", { enumerable: true, get: function () { return auth_js_1.AuthManager; } });
|
|
21
21
|
Object.defineProperty(exports, "buildAuthUrl", { enumerable: true, get: function () { return auth_js_1.buildAuthUrl; } });
|
|
22
|
+
Object.defineProperty(exports, "buildFederatedLogoutUrl", { enumerable: true, get: function () { return auth_js_1.buildFederatedLogoutUrl; } });
|
|
22
23
|
Object.defineProperty(exports, "clearTestingToken", { enumerable: true, get: function () { return auth_js_1.clearTestingToken; } });
|
|
23
24
|
Object.defineProperty(exports, "getTestingToken", { enumerable: true, get: function () { return auth_js_1.getTestingToken; } });
|
|
24
25
|
Object.defineProperty(exports, "resolveSafeRedirectUri", { enumerable: true, get: function () { return auth_js_1.resolveSafeRedirectUri; } });
|
|
@@ -198,6 +199,7 @@ exports.setTestingToken = setTestingToken;
|
|
|
198
199
|
exports.getTestingToken = getTestingToken;
|
|
199
200
|
exports.clearTestingToken = clearTestingToken;
|
|
200
201
|
exports.buildAuthUrl = buildAuthUrl;
|
|
202
|
+
exports.buildFederatedLogoutUrl = buildFederatedLogoutUrl;
|
|
201
203
|
exports.resolveSafeRedirectUri = resolveSafeRedirectUri;
|
|
202
204
|
const session_1 = require("supertokens-web-js/recipe/session");
|
|
203
205
|
const supertokens_js_1 = require("./supertokens.js");
|
|
@@ -305,6 +307,26 @@ function buildAuthUrl(authUrl, options = {}) {
|
|
|
305
307
|
}
|
|
306
308
|
return url.toString();
|
|
307
309
|
}
|
|
310
|
+
function buildFederatedLogoutUrl(authUrl, options = {}) {
|
|
311
|
+
const url = new URL(authUrl);
|
|
312
|
+
url.pathname = resolveAuthPath(url.pathname, options.path ?? "logout");
|
|
313
|
+
for (const [key, value] of Object.entries(options.params ?? {})) {
|
|
314
|
+
if (value === null || value === undefined)
|
|
315
|
+
continue;
|
|
316
|
+
if (Array.isArray(value)) {
|
|
317
|
+
url.searchParams.delete(key);
|
|
318
|
+
for (const item of value) {
|
|
319
|
+
url.searchParams.append(key, String(item));
|
|
320
|
+
}
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
url.searchParams.set(key, String(value));
|
|
324
|
+
}
|
|
325
|
+
if (options.redirectUri && options.redirectUri.trim()) {
|
|
326
|
+
url.searchParams.set(options.redirectParam ?? "redirect_uri", options.redirectUri);
|
|
327
|
+
}
|
|
328
|
+
return url.toString();
|
|
329
|
+
}
|
|
308
330
|
function resolveSafeRedirectUri(rawValue, options) {
|
|
309
331
|
const siteOrigin = normalizeOrigin(options.siteOrigin);
|
|
310
332
|
const blockedPaths = options.blockedPaths ?? DEFAULT_BLOCKED_REDIRECT_PATHS;
|
|
@@ -549,6 +571,12 @@ class AuthManager {
|
|
|
549
571
|
getAuthUrl(options = {}) {
|
|
550
572
|
return buildAuthUrl(this.authUrl, options);
|
|
551
573
|
}
|
|
574
|
+
/**
|
|
575
|
+
* Build upstream/federated logout URL.
|
|
576
|
+
*/
|
|
577
|
+
getFederatedLogoutUrl(options = {}) {
|
|
578
|
+
return buildFederatedLogoutUrl(this.authUrl, options);
|
|
579
|
+
}
|
|
552
580
|
/**
|
|
553
581
|
* Redirect to the auth service, passing the current URL as redirect_uri.
|
|
554
582
|
* After the user authenticates, the auth service should redirect back to
|
|
@@ -561,6 +589,23 @@ class AuthManager {
|
|
|
561
589
|
const redirectUri = options.redirectUri ?? window.location.href;
|
|
562
590
|
window.location.href = this.getAuthUrl({ ...options, redirectUri });
|
|
563
591
|
}
|
|
592
|
+
/**
|
|
593
|
+
* Optional full logout flow:
|
|
594
|
+
* 1. clear local SDK/session cookies
|
|
595
|
+
* 2. redirect to auth service logout endpoint to terminate upstream SSO
|
|
596
|
+
*/
|
|
597
|
+
async redirectToFederatedLogout(options = {}) {
|
|
598
|
+
this.assertBrowserContext();
|
|
599
|
+
const redirectUri = options.redirectUri ?? window.location.href;
|
|
600
|
+
const localSignOut = options.localSignOut ?? true;
|
|
601
|
+
if (localSignOut) {
|
|
602
|
+
await this.signOut();
|
|
603
|
+
}
|
|
604
|
+
window.location.href = this.getFederatedLogoutUrl({
|
|
605
|
+
...options,
|
|
606
|
+
redirectUri,
|
|
607
|
+
});
|
|
608
|
+
}
|
|
564
609
|
}
|
|
565
610
|
exports.AuthManager = AuthManager;
|
|
566
611
|
|
|
@@ -1538,6 +1583,9 @@ class ConversationsNamespace {
|
|
|
1538
1583
|
listByAssistant(assistantId, options = {}) {
|
|
1539
1584
|
return this.list({ ...options, assistant_id: assistantId });
|
|
1540
1585
|
}
|
|
1586
|
+
listModels() {
|
|
1587
|
+
return this.http.request("GET", "/models");
|
|
1588
|
+
}
|
|
1541
1589
|
create(payload) {
|
|
1542
1590
|
return this.http.request("POST", "/conversations", {
|
|
1543
1591
|
body: {
|
package/dist/browser.d.ts
CHANGED
|
@@ -9,5 +9,5 @@
|
|
|
9
9
|
* </script>
|
|
10
10
|
*/
|
|
11
11
|
export { LemmaClient } from "./client.js";
|
|
12
|
-
export { AuthManager, buildAuthUrl, clearTestingToken, getTestingToken, resolveSafeRedirectUri, setTestingToken, } from "./auth.js";
|
|
12
|
+
export { AuthManager, buildAuthUrl, buildFederatedLogoutUrl, clearTestingToken, getTestingToken, resolveSafeRedirectUri, setTestingToken, } from "./auth.js";
|
|
13
13
|
export { ApiError } from "./http.js";
|
package/dist/browser.js
CHANGED
|
@@ -9,5 +9,5 @@
|
|
|
9
9
|
* </script>
|
|
10
10
|
*/
|
|
11
11
|
export { LemmaClient } from "./client.js";
|
|
12
|
-
export { AuthManager, buildAuthUrl, clearTestingToken, getTestingToken, resolveSafeRedirectUri, setTestingToken, } from "./auth.js";
|
|
12
|
+
export { AuthManager, buildAuthUrl, buildFederatedLogoutUrl, clearTestingToken, getTestingToken, resolveSafeRedirectUri, setTestingToken, } from "./auth.js";
|
|
13
13
|
export { ApiError } from "./http.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { LemmaClient } from "./client.js";
|
|
2
2
|
export type { LemmaConfig } from "./client.js";
|
|
3
|
-
export { AuthManager, buildAuthUrl, clearTestingToken, getTestingToken, resolveSafeRedirectUri, setTestingToken, } from "./auth.js";
|
|
4
|
-
export type { AuthState, AuthListener, AuthStatus, UserInfo, AuthRedirectMode, BuildAuthUrlOptions, ResolveSafeRedirectUriOptions, } from "./auth.js";
|
|
3
|
+
export { AuthManager, buildAuthUrl, buildFederatedLogoutUrl, clearTestingToken, getTestingToken, resolveSafeRedirectUri, setTestingToken, } from "./auth.js";
|
|
4
|
+
export type { AuthState, AuthListener, AuthStatus, UserInfo, AuthRedirectMode, BuildAuthUrlOptions, BuildFederatedLogoutUrlOptions, RedirectToFederatedLogoutOptions, ResolveSafeRedirectUriOptions, } from "./auth.js";
|
|
5
5
|
export { ApiError } from "./http.js";
|
|
6
6
|
export * from "./types.js";
|
|
7
7
|
export { readSSE, parseSSEJson } from "./streams.js";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { LemmaClient } from "./client.js";
|
|
2
|
-
export { AuthManager, buildAuthUrl, clearTestingToken, getTestingToken, resolveSafeRedirectUri, setTestingToken, } from "./auth.js";
|
|
2
|
+
export { AuthManager, buildAuthUrl, buildFederatedLogoutUrl, clearTestingToken, getTestingToken, resolveSafeRedirectUri, setTestingToken, } from "./auth.js";
|
|
3
3
|
export { ApiError } from "./http.js";
|
|
4
4
|
export * from "./types.js";
|
|
5
5
|
export { readSSE, parseSSEJson } from "./streams.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { HttpClient } from "../http.js";
|
|
2
|
+
import type { AvailableModelsListResponse } from "../openapi_client/models/AvailableModelsListResponse.js";
|
|
2
3
|
import type { AssistantListResponse } from "../openapi_client/models/AssistantListResponse.js";
|
|
3
4
|
import type { AssistantResponse } from "../openapi_client/models/AssistantResponse.js";
|
|
4
5
|
import type { ConversationListResponse } from "../openapi_client/models/ConversationListResponse.js";
|
|
@@ -41,6 +42,7 @@ export declare class ConversationsNamespace {
|
|
|
41
42
|
limit?: number;
|
|
42
43
|
page_token?: string;
|
|
43
44
|
}): Promise<ConversationListResponse>;
|
|
45
|
+
listModels(): Promise<AvailableModelsListResponse>;
|
|
44
46
|
create(payload: CreateConversationRequest): Promise<ConversationResponse>;
|
|
45
47
|
createForAssistant(assistantId: string, payload?: Omit<CreateConversationRequest, "assistant_id">): Promise<ConversationResponse>;
|
|
46
48
|
get(conversationId: string, options?: {
|
|
@@ -67,6 +67,9 @@ export class ConversationsNamespace {
|
|
|
67
67
|
listByAssistant(assistantId, options = {}) {
|
|
68
68
|
return this.list({ ...options, assistant_id: assistantId });
|
|
69
69
|
}
|
|
70
|
+
listModels() {
|
|
71
|
+
return this.http.request("GET", "/models");
|
|
72
|
+
}
|
|
70
73
|
create(payload) {
|
|
71
74
|
return this.http.request("POST", "/conversations", {
|
|
72
75
|
body: {
|
|
@@ -26,7 +26,9 @@ export type { AssistantListResponse } from './models/AssistantListResponse.js';
|
|
|
26
26
|
export type { AssistantResponse } from './models/AssistantResponse.js';
|
|
27
27
|
export type { AssistantSurfaceListResponse } from './models/AssistantSurfaceListResponse.js';
|
|
28
28
|
export type { AssistantSurfaceResponse } from './models/AssistantSurfaceResponse.js';
|
|
29
|
+
export type { AvailableModelInfo } from './models/AvailableModelInfo.js';
|
|
29
30
|
export { AvailableModels } from './models/AvailableModels.js';
|
|
31
|
+
export type { AvailableModelsListResponse } from './models/AvailableModelsListResponse.js';
|
|
30
32
|
export { BillingInterval } from './models/BillingInterval.js';
|
|
31
33
|
export type { Body_upload_file_files__resource_type___resource_id__upload_post } from './models/Body_upload_file_files__resource_type___resource_id__upload_post.js';
|
|
32
34
|
export type { BulkCreateRecordsRequest } from './models/BulkCreateRecordsRequest.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -4,8 +4,7 @@ export declare enum AvailableModels {
|
|
|
4
4
|
GEMINI_FLASH_LITE = "GEMINI_FLASH_LITE",
|
|
5
5
|
KIMI_K2 = "KIMI_K2",
|
|
6
6
|
GPT_OSS = "GPT_OSS",
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
GLM_5 = "GLM_5"
|
|
7
|
+
DEEPSEEK_V32 = "DEEPSEEK_V32",
|
|
8
|
+
GLM_5 = "GLM_5",
|
|
9
|
+
QWEN3_6 = "QWEN3_6"
|
|
11
10
|
}
|
|
@@ -9,8 +9,7 @@ export var AvailableModels;
|
|
|
9
9
|
AvailableModels["GEMINI_FLASH_LITE"] = "GEMINI_FLASH_LITE";
|
|
10
10
|
AvailableModels["KIMI_K2"] = "KIMI_K2";
|
|
11
11
|
AvailableModels["GPT_OSS"] = "GPT_OSS";
|
|
12
|
-
AvailableModels["
|
|
13
|
-
AvailableModels["CLAUDE_SONNET_4"] = "CLAUDE_SONNET_4";
|
|
14
|
-
AvailableModels["QWEN3_235B"] = "QWEN3_235B";
|
|
12
|
+
AvailableModels["DEEPSEEK_V32"] = "DEEPSEEK_V32";
|
|
15
13
|
AvailableModels["GLM_5"] = "GLM_5";
|
|
14
|
+
AvailableModels["QWEN3_6"] = "QWEN3_6";
|
|
16
15
|
})(AvailableModels || (AvailableModels = {}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AvailableModelsListResponse } from '../models/AvailableModelsListResponse.js';
|
|
1
2
|
import type { ConversationListResponse } from '../models/ConversationListResponse.js';
|
|
2
3
|
import type { ConversationMessageListResponse } from '../models/ConversationMessageListResponse.js';
|
|
3
4
|
import type { ConversationResponse } from '../models/ConversationResponse.js';
|
|
@@ -76,4 +77,11 @@ export declare class ConversationsService {
|
|
|
76
77
|
* @throws ApiError
|
|
77
78
|
*/
|
|
78
79
|
static conversationStreamResume(conversationId: string, podId?: (string | null)): CancelablePromise<any>;
|
|
80
|
+
/**
|
|
81
|
+
* List Available Models
|
|
82
|
+
* Get list of all available models in the system.
|
|
83
|
+
* @returns AvailableModelsListResponse Successful Response
|
|
84
|
+
* @throws ApiError
|
|
85
|
+
*/
|
|
86
|
+
static conversationModelsList(): CancelablePromise<AvailableModelsListResponse>;
|
|
79
87
|
}
|
|
@@ -185,4 +185,16 @@ export class ConversationsService {
|
|
|
185
185
|
},
|
|
186
186
|
});
|
|
187
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* List Available Models
|
|
190
|
+
* Get list of all available models in the system.
|
|
191
|
+
* @returns AvailableModelsListResponse Successful Response
|
|
192
|
+
* @throws ApiError
|
|
193
|
+
*/
|
|
194
|
+
static conversationModelsList() {
|
|
195
|
+
return __request(OpenAPI, {
|
|
196
|
+
method: 'GET',
|
|
197
|
+
url: '/models',
|
|
198
|
+
});
|
|
199
|
+
}
|
|
188
200
|
}
|
|
@@ -773,7 +773,15 @@ export function AssistantExperienceView({ controller, title = "Lemma Assistant",
|
|
|
773
773
|
const isPinnedToBottomRef = useRef(true);
|
|
774
774
|
const loadingOlderFromScrollRef = useRef(false);
|
|
775
775
|
const isConversationBusy = controller.isLoading || controller.isActiveConversationRunning;
|
|
776
|
-
const availableModels = useMemo(() =>
|
|
776
|
+
const availableModels = useMemo(() => {
|
|
777
|
+
const dynamicModels = controller.availableModels
|
|
778
|
+
.map((model) => model.id)
|
|
779
|
+
.filter((model) => model.trim().length > 0);
|
|
780
|
+
return dynamicModels.length > 0
|
|
781
|
+
? dynamicModels
|
|
782
|
+
: Object.values(AvailableModels);
|
|
783
|
+
}, [controller.availableModels]);
|
|
784
|
+
const availableModelLabels = useMemo(() => new Map(controller.availableModels.map((model) => [model.id, model.name])), [controller.availableModels]);
|
|
777
785
|
const resizeComposer = useCallback(() => {
|
|
778
786
|
const textarea = inputRef.current;
|
|
779
787
|
if (!textarea)
|
|
@@ -1027,7 +1035,7 @@ export function AssistantExperienceView({ controller, title = "Lemma Assistant",
|
|
|
1027
1035
|
return (_jsxs("div", { className: "lemma-assistant-experience", "data-chrome-style": chromeStyle, "data-status-placement": statusPlacement, "data-radius": radius, "data-show-model-picker": showModelPicker ? "true" : "false", "data-busy": isConversationBusy ? "true" : "false", "data-has-plan": planSummary ? "true" : "false", "data-has-pending-files": controller.pendingFiles.length > 0 ? "true" : "false", "data-show-conversation-list": showConversationList ? "true" : "false", children: [showConversationList ? (_jsxs("aside", { className: "lemma-assistant-experience-sidebar", children: [_jsx("div", { className: "lemma-assistant-experience-sidebar-header", children: _jsxs("div", { className: "lemma-assistant-experience-sidebar-header-row", children: [_jsxs("div", { className: "lemma-assistant-experience-sidebar-copy", children: [_jsx("div", { className: "lemma-assistant-experience-sidebar-title", children: "Conversations" }), _jsxs("div", { className: "lemma-assistant-experience-sidebar-meta", children: [controller.conversations.length, " total"] })] }), showNewConversationButton ? (_jsx("button", { type: "button", onClick: controller.clearMessages, className: "lemma-assistant-experience-sidebar-new", children: "New" })) : null] }) }), _jsx("div", { className: "lemma-assistant-experience-sidebar-items", children: controller.conversations.map((conversation) => {
|
|
1028
1036
|
const isActive = conversation.id === controller.activeConversationId;
|
|
1029
1037
|
return (_jsxs("button", { type: "button", onClick: () => controller.selectConversation(conversation.id), className: cx("lemma-assistant-experience-sidebar-item", isActive && "lemma-assistant-experience-sidebar-item-active"), children: [_jsx("div", { className: "lemma-assistant-experience-sidebar-item-title", children: renderConversationLabel({ conversation, isActive }) }), _jsx("div", { className: "lemma-assistant-experience-sidebar-item-status", children: (conversation.status || "waiting").toLowerCase() })] }, conversation.id));
|
|
1030
|
-
}) })] })) : null, _jsxs("div", { className: "lemma-assistant-experience-main", children: [_jsxs("div", { className: "lemma-assistant-experience-card", children: [_jsx(AssistantHeader, { className: "lemma-assistant-experience-header", tone: headerTone, title: title, subtitle: subtitle, badge: _jsx("span", { className: "lemma-assistant-experience-header-badge-icon", children: "\u2728" }), controls: showModelPicker || showNewConversationButton ? (_jsxs(_Fragment, { children: [showModelPicker ? (_jsx(AssistantModelPicker, { value: controller.conversationModel, options: availableModels, onChange: (nextModel) => { void handleModelChange(nextModel); }, disabled: isConversationBusy || isUpdatingModel, autoLabel: "Auto", className: "lemma-assistant-experience-model-picker" })) : null, showNewConversationButton ? (_jsx("button", { type: "button", onClick: controller.clearMessages, title: "New conversation", className: "lemma-assistant-experience-new", children: "\u21BA" })) : null] })) : undefined }), _jsxs(AssistantMessageViewport, { className: "lemma-assistant-experience-viewport", ref: messagesContainerRef, onScroll: updatePinnedState, children: [controller.messages.length === 0 && !isConversationBusy ? (emptyState || (_jsx(EmptyState, { onSendMessage: (message) => { void controller.sendMessage(message); }, suggestions: emptyStateSuggestions }))) : null, (controller.isLoadingMessages && controller.messages.length === 0) ? (_jsx("div", { className: "lemma-assistant-experience-loading", children: _jsx("span", { className: "lemma-assistant-experience-loading-text", children: "Loading\u2026" }) })) : null, (controller.isLoadingOlderMessages && controller.messages.length > 0) ? (_jsx("div", { className: "lemma-assistant-experience-loading-older", children: _jsx("span", { className: "lemma-assistant-experience-loading-older-text", children: "Loading older\u2026" }) })) : null, displayMessageRows.map((row, index) => {
|
|
1038
|
+
}) })] })) : null, _jsxs("div", { className: "lemma-assistant-experience-main", children: [_jsxs("div", { className: "lemma-assistant-experience-card", children: [_jsx(AssistantHeader, { className: "lemma-assistant-experience-header", tone: headerTone, title: title, subtitle: subtitle, badge: _jsx("span", { className: "lemma-assistant-experience-header-badge-icon", children: "\u2728" }), controls: showModelPicker || showNewConversationButton ? (_jsxs(_Fragment, { children: [showModelPicker ? (_jsx(AssistantModelPicker, { value: controller.conversationModel, options: availableModels, getOptionLabel: (model) => availableModelLabels.get(model) ?? model, onChange: (nextModel) => { void handleModelChange(nextModel); }, disabled: isConversationBusy || isUpdatingModel, autoLabel: "Auto", className: "lemma-assistant-experience-model-picker" })) : null, showNewConversationButton ? (_jsx("button", { type: "button", onClick: controller.clearMessages, title: "New conversation", className: "lemma-assistant-experience-new", children: "\u21BA" })) : null] })) : undefined }), _jsxs(AssistantMessageViewport, { className: "lemma-assistant-experience-viewport", ref: messagesContainerRef, onScroll: updatePinnedState, children: [controller.messages.length === 0 && !isConversationBusy ? (emptyState || (_jsx(EmptyState, { onSendMessage: (message) => { void controller.sendMessage(message); }, suggestions: emptyStateSuggestions }))) : null, (controller.isLoadingMessages && controller.messages.length === 0) ? (_jsx("div", { className: "lemma-assistant-experience-loading", children: _jsx("span", { className: "lemma-assistant-experience-loading-text", children: "Loading\u2026" }) })) : null, (controller.isLoadingOlderMessages && controller.messages.length > 0) ? (_jsx("div", { className: "lemma-assistant-experience-loading-older", children: _jsx("span", { className: "lemma-assistant-experience-loading-older-text", children: "Loading older\u2026" }) })) : null, displayMessageRows.map((row, index) => {
|
|
1031
1039
|
const previousRow = index > 0 ? displayMessageRows[index - 1] : null;
|
|
1032
1040
|
const showAssistantHeader = row.message.role !== "assistant"
|
|
1033
1041
|
? false
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
+
import type { AvailableModelInfo } from "../../types.js";
|
|
2
3
|
import type { AssistantRenderableMessage, AssistantToolInvocation } from "../useAssistantController.js";
|
|
3
4
|
export interface AssistantConversationListItem {
|
|
4
5
|
id: string;
|
|
@@ -11,6 +12,7 @@ export interface AssistantControllerView {
|
|
|
11
12
|
messages: AssistantRenderableMessage[];
|
|
12
13
|
conversations: AssistantConversationListItem[];
|
|
13
14
|
activeConversationId: string | null;
|
|
15
|
+
availableModels: AvailableModelInfo[];
|
|
14
16
|
conversationModel: string | null;
|
|
15
17
|
setConversationModel(model: string | null): Promise<void>;
|
|
16
18
|
isActiveConversationRunning: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LemmaClient } from "../client.js";
|
|
2
|
-
import type { Conversation, ConversationModel } from "../types.js";
|
|
2
|
+
import type { AvailableModelInfo, Conversation, ConversationModel } from "../types.js";
|
|
3
3
|
export interface AssistantConversationScope {
|
|
4
4
|
podId?: string | null;
|
|
5
5
|
assistantId?: string | null;
|
|
@@ -54,6 +54,7 @@ export interface UseAssistantControllerResult {
|
|
|
54
54
|
messages: AssistantRenderableMessage[];
|
|
55
55
|
conversations: Conversation[];
|
|
56
56
|
activeConversationId: string | null;
|
|
57
|
+
availableModels: AvailableModelInfo[];
|
|
57
58
|
conversationModel: ConversationModel | null;
|
|
58
59
|
isActiveConversationRunning: boolean;
|
|
59
60
|
isLoading: boolean;
|
|
@@ -543,6 +543,7 @@ export function useAssistantController({ client, podId, assistantId, organizatio
|
|
|
543
543
|
const [messages, setMessages] = useState([]);
|
|
544
544
|
const [conversations, setConversations] = useState([]);
|
|
545
545
|
const [activeConversationId, setActiveConversationId] = useState(null);
|
|
546
|
+
const [availableModels, setAvailableModels] = useState([]);
|
|
546
547
|
const [conversationModel, setConversationModelState] = useState(null);
|
|
547
548
|
const [isStreaming, setIsStreaming] = useState(false);
|
|
548
549
|
const [isLoadingConversations, setIsLoadingConversations] = useState(false);
|
|
@@ -649,6 +650,15 @@ export function useAssistantController({ client, podId, assistantId, organizatio
|
|
|
649
650
|
setIsLoadingConversations(false);
|
|
650
651
|
}
|
|
651
652
|
}, [scope, sessionListConversations]);
|
|
653
|
+
const loadAvailableModels = useCallback(async () => {
|
|
654
|
+
try {
|
|
655
|
+
const response = await client.conversations.listModels();
|
|
656
|
+
return response.items ?? [];
|
|
657
|
+
}
|
|
658
|
+
catch {
|
|
659
|
+
return [];
|
|
660
|
+
}
|
|
661
|
+
}, [client]);
|
|
652
662
|
const loadConversationMessages = useCallback(async (conversationId) => {
|
|
653
663
|
setIsLoadingMessages(true);
|
|
654
664
|
try {
|
|
@@ -706,6 +716,23 @@ export function useAssistantController({ client, podId, assistantId, organizatio
|
|
|
706
716
|
useEffect(() => {
|
|
707
717
|
conversationsRef.current = conversations;
|
|
708
718
|
}, [conversations]);
|
|
719
|
+
useEffect(() => {
|
|
720
|
+
if (!enabled) {
|
|
721
|
+
setAvailableModels([]);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
let cancelled = false;
|
|
725
|
+
void loadAvailableModels()
|
|
726
|
+
.then((models) => {
|
|
727
|
+
if (cancelled)
|
|
728
|
+
return;
|
|
729
|
+
setAvailableModels(models);
|
|
730
|
+
})
|
|
731
|
+
.catch(() => undefined);
|
|
732
|
+
return () => {
|
|
733
|
+
cancelled = true;
|
|
734
|
+
};
|
|
735
|
+
}, [enabled, loadAvailableModels]);
|
|
709
736
|
useEffect(() => {
|
|
710
737
|
const conversationId = activeConversationIdRef.current;
|
|
711
738
|
if (!conversationId) {
|
|
@@ -759,6 +786,7 @@ export function useAssistantController({ client, podId, assistantId, organizatio
|
|
|
759
786
|
loadingConversationIdRef.current = null;
|
|
760
787
|
skipInitialLoadConversationIdsRef.current.clear();
|
|
761
788
|
setActiveConversationId(null);
|
|
789
|
+
setAvailableModels([]);
|
|
762
790
|
setConversationModelState(null);
|
|
763
791
|
setConversations([]);
|
|
764
792
|
setMessages([]);
|
|
@@ -842,8 +870,12 @@ export function useAssistantController({ client, podId, assistantId, organizatio
|
|
|
842
870
|
const conversationIsRunning = isConversationRunning(activeConversation?.status);
|
|
843
871
|
if (!hadActiveStream && !conversationIsRunning)
|
|
844
872
|
return;
|
|
873
|
+
const previousStatus = activeConversation?.status;
|
|
845
874
|
touchConversation(conversationId, { status: "waiting" });
|
|
846
|
-
void sessionStop(conversationId).catch(() =>
|
|
875
|
+
void sessionStop(conversationId).catch((error) => {
|
|
876
|
+
touchConversation(conversationId, { status: previousStatus });
|
|
877
|
+
setLocalError((prev) => prev || (error instanceof Error ? error.message : "Failed to stop conversation"));
|
|
878
|
+
});
|
|
847
879
|
}, [isStreaming, sessionCancel, sessionIsStreaming, sessionStop, touchConversation]);
|
|
848
880
|
const selectConversation = useCallback((conversationId) => {
|
|
849
881
|
if (sessionIsStreaming || isStreaming) {
|
|
@@ -1066,6 +1098,7 @@ export function useAssistantController({ client, podId, assistantId, organizatio
|
|
|
1066
1098
|
messages,
|
|
1067
1099
|
conversations,
|
|
1068
1100
|
activeConversationId,
|
|
1101
|
+
availableModels,
|
|
1069
1102
|
conversationModel,
|
|
1070
1103
|
isActiveConversationRunning,
|
|
1071
1104
|
isLoading,
|
|
@@ -21,6 +21,7 @@ function messageTime(message) {
|
|
|
21
21
|
function isOptimisticId(messageId) {
|
|
22
22
|
return messageId.startsWith("optimistic-user-");
|
|
23
23
|
}
|
|
24
|
+
const OPTIMISTIC_MATCH_WINDOW_MS = 2 * 60 * 1000;
|
|
24
25
|
function upsertRuntimeMessage(previous, incoming) {
|
|
25
26
|
const next = [...previous];
|
|
26
27
|
const directIndex = next.findIndex((message) => message.id === incoming.id);
|
|
@@ -31,9 +32,22 @@ function upsertRuntimeMessage(previous, incoming) {
|
|
|
31
32
|
if (incoming.role === "user") {
|
|
32
33
|
const incomingText = messageText(incoming.content);
|
|
33
34
|
if (incomingText) {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
const incomingTimestamp = messageTime(incoming);
|
|
36
|
+
let optimisticIndex = -1;
|
|
37
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
38
|
+
next.forEach((message, index) => {
|
|
39
|
+
if (message.role !== "user"
|
|
40
|
+
|| !isOptimisticId(message.id)
|
|
41
|
+
|| messageText(message.content) !== incomingText) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const distance = Math.abs(messageTime(message) - incomingTimestamp);
|
|
45
|
+
if (distance > OPTIMISTIC_MATCH_WINDOW_MS || distance >= bestDistance) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
optimisticIndex = index;
|
|
49
|
+
bestDistance = distance;
|
|
50
|
+
});
|
|
37
51
|
if (optimisticIndex >= 0) {
|
|
38
52
|
next[optimisticIndex] = incoming;
|
|
39
53
|
return next;
|
|
@@ -71,7 +85,14 @@ export function useAssistantRuntime({ conversationId = null, sessionMessages = [
|
|
|
71
85
|
const normalized = messages
|
|
72
86
|
.map((message) => toRuntimeMessage(message, conversationId))
|
|
73
87
|
.filter((message) => !conversationId || message.conversation_id === conversationId);
|
|
74
|
-
setRuntimeMessages(
|
|
88
|
+
setRuntimeMessages((previous) => {
|
|
89
|
+
const scopedPrevious = previous.filter((message) => !conversationId || message.conversation_id === conversationId);
|
|
90
|
+
// Loads can complete after optimistic appends or stream events. Merge the
|
|
91
|
+
// loaded snapshot into the current runtime state so newer local messages
|
|
92
|
+
// are not temporarily dropped while the server catches up.
|
|
93
|
+
const merged = normalized.reduce((accumulator, message) => upsertRuntimeMessage(accumulator, message), scopedPrevious);
|
|
94
|
+
return [...merged].sort((a, b) => messageTime(a) - messageTime(b));
|
|
95
|
+
});
|
|
75
96
|
}, [conversationId]);
|
|
76
97
|
const appendOptimisticUserMessage = useCallback((content, options) => {
|
|
77
98
|
const trimmed = content.trim();
|
|
@@ -393,12 +393,21 @@ export function useAssistantSession(options) {
|
|
|
393
393
|
return false;
|
|
394
394
|
}
|
|
395
395
|
}
|
|
396
|
+
const previousResumeKey = autoResumedKeyRef.current;
|
|
396
397
|
autoResumedKeyRef.current = resumeKey;
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
398
|
+
try {
|
|
399
|
+
await resume({
|
|
400
|
+
conversationId: id,
|
|
401
|
+
onlyIfRunning: true,
|
|
402
|
+
});
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
if (autoResumedKeyRef.current === resumeKey) {
|
|
407
|
+
autoResumedKeyRef.current = previousResumeKey;
|
|
408
|
+
}
|
|
409
|
+
throw error;
|
|
410
|
+
}
|
|
402
411
|
}, [conversationId, isStreaming, refreshConversation, resume]);
|
|
403
412
|
const stop = useCallback(async (explicitConversationId) => {
|
|
404
413
|
const id = requireConversationId(explicitConversationId ?? conversationId);
|
package/dist/types.d.ts
CHANGED
|
@@ -40,7 +40,7 @@ export type CreateAssistantInput = CreateAssistantRequest;
|
|
|
40
40
|
export type UpdateAssistantInput = UpdateAssistantRequest;
|
|
41
41
|
export type Conversation = ConversationResponse;
|
|
42
42
|
export type ConversationMessage = ConversationMessageResponse;
|
|
43
|
-
export type ConversationModel = `${AvailableModels}
|
|
43
|
+
export type ConversationModel = `${AvailableModels}` | (string & {});
|
|
44
44
|
export type Task = TaskResponse;
|
|
45
45
|
export type TaskMessage = TaskMessageResponse;
|
|
46
46
|
export type FunctionRun = FunctionRunResponse;
|