@datalayer/agent-runtimes 1.5.27 → 1.5.30
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/dist-embed/chunks/ChatView-C_NOLqwI.js +32 -0
- package/dist-embed/datalayer-app-main.js +103 -93
- package/lib/apps/apps/AppKept.js +5 -1
- package/lib/apps/apps/appspec.d.ts +0 -5
- package/lib/apps/apps/appspec.js +1 -6
- package/lib/apps/apps/checks.js +4 -9
- package/lib/apps/embed/element.js +24 -7
- package/lib/apps/embed/embedConfig.d.ts +11 -2
- package/lib/apps/embed/embedConfig.js +26 -2
- package/lib/apps/embed/visitorApp.d.ts +9 -2
- package/lib/apps/embed/visitorApp.js +9 -2
- package/lib/apps/plugins/chat/ChatView.js +6 -3
- package/lib/chat/assistant/AssistantStage.js +17 -0
- package/lib/chat/assistant/clearance.d.ts +10 -0
- package/lib/chat/assistant/clearance.js +14 -0
- package/lib/chat/base/ChatBase.js +12 -3
- package/lib/specs/appspecSchema.js +2 -2
- package/lib/specs/voices.js +89 -0
- package/lib/types/agentspecs.d.ts +1 -1
- package/lib/voice/ComposerVoice.d.ts +10 -2
- package/lib/voice/ComposerVoice.js +35 -5
- package/lib/voice/HandsFree.d.ts +55 -0
- package/lib/voice/HandsFree.js +211 -0
- package/lib/voice/VoiceInput.d.ts +2 -2
- package/lib/voice/VoiceInput.js +6 -4
- package/lib/voice/VoiceMenu.js +2 -1
- package/lib/voice/chatVoice.d.ts +6 -4
- package/lib/voice/chatVoice.js +11 -12
- package/lib/voice/consent.d.ts +8 -0
- package/lib/voice/consent.js +10 -0
- package/lib/voice/handsFree.d.ts +58 -0
- package/lib/voice/handsFree.js +48 -0
- package/lib/voice/hearing.d.ts +23 -1
- package/lib/voice/hearing.js +18 -3
- package/lib/voice/index.d.ts +21 -8
- package/lib/voice/index.js +3 -0
- package/lib/voice/serverHearing.d.ts +58 -0
- package/lib/voice/serverHearing.js +136 -0
- package/lib/voice/settings.d.ts +5 -0
- package/lib/voice/settings.js +8 -0
- package/lib/voice/speaker.d.ts +4 -3
- package/lib/voice/speaker.js +1 -2
- package/lib/voice/useVoiceSpeaker.js +0 -3
- package/package.json +2 -2
- package/scripts/voice/measure.py +74 -0
- package/dist-embed/chunks/ChatView-CUWE6I5M.js +0 -32
package/lib/voice/index.d.ts
CHANGED
|
@@ -13,6 +13,9 @@ export * from './sentences';
|
|
|
13
13
|
export * from './pinned';
|
|
14
14
|
export * from './capture';
|
|
15
15
|
export * from './hearing';
|
|
16
|
+
export * from './serverHearing';
|
|
17
|
+
export * from './handsFree';
|
|
18
|
+
export { HandsFree, type HandsFreeProps } from './HandsFree';
|
|
16
19
|
export * from './speaker';
|
|
17
20
|
export * from './consent';
|
|
18
21
|
export * from './settings';
|
|
@@ -46,8 +49,14 @@ export type { VoiceSpeaker } from './useVoiceSpeaker';
|
|
|
46
49
|
export interface ChatVoice {
|
|
47
50
|
/** The language listened to and spoken, BCP 47. */
|
|
48
51
|
language: string;
|
|
49
|
-
/**
|
|
50
|
-
|
|
52
|
+
/**
|
|
53
|
+
* `push_to_talk`: the microphone in the composer; `hands_free`: that, and
|
|
54
|
+
* *Talk with it* beside it — a conversation by voice (VO-12, VO-13);
|
|
55
|
+
* `off`: none.
|
|
56
|
+
*/
|
|
57
|
+
input: 'off' | 'push_to_talk' | 'hands_free';
|
|
58
|
+
/** The silence after speech that ends a hands-free turn, in ms (VO-12): 700 when unsaid. */
|
|
59
|
+
turnSilenceMs?: number;
|
|
51
60
|
/**
|
|
52
61
|
* When answers are heard: `always`, as they are written; `on_request`, a
|
|
53
62
|
* *Read aloud* under each answer (VO-21); or `off`, the default.
|
|
@@ -64,15 +73,19 @@ export interface ChatVoice {
|
|
|
64
73
|
* (VO-10, VO-69): 1,500 when unsaid; longer for a person who pauses.
|
|
65
74
|
*/
|
|
66
75
|
silenceMs?: number;
|
|
67
|
-
/** ai-agents, whose speech service says the answers. */
|
|
76
|
+
/** ai-agents, whose speech service says the answers, and hears where the browser does not. */
|
|
68
77
|
speechUrl?: string;
|
|
69
|
-
/** The token the speech service is asked with. */
|
|
70
|
-
token?: string;
|
|
71
78
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
79
|
+
* Where what is said is heard (§5): `device`, the browser; `server`,
|
|
80
|
+
* Datalayer's speech service; `auto`, the default, the browser where a
|
|
81
|
+
* model there hears the language, else the server. The Appspec's
|
|
82
|
+
* `interface.voice.where`.
|
|
74
83
|
*/
|
|
75
|
-
|
|
84
|
+
where?: 'auto' | 'device' | 'server';
|
|
85
|
+
/** The person asked for their language to be found by Whisper on the server (VO-14). */
|
|
86
|
+
detect?: boolean;
|
|
87
|
+
/** The token the speech service is asked with. */
|
|
88
|
+
token?: string;
|
|
76
89
|
/** Send what is said at once, rather than put it in the composer. */
|
|
77
90
|
sendWhatISay?: boolean;
|
|
78
91
|
/**
|
package/lib/voice/index.js
CHANGED
|
@@ -7,6 +7,9 @@ export * from './sentences';
|
|
|
7
7
|
export * from './pinned';
|
|
8
8
|
export * from './capture';
|
|
9
9
|
export * from './hearing';
|
|
10
|
+
export * from './serverHearing';
|
|
11
|
+
export * from './handsFree';
|
|
12
|
+
export { HandsFree } from './HandsFree';
|
|
10
13
|
export * from './speaker';
|
|
11
14
|
export * from './consent';
|
|
12
15
|
export * from './settings';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Hearing, HearingProgress, HeardSoFar } from './hearing';
|
|
2
|
+
import type { Transcript } from './types';
|
|
3
|
+
/** Where the speech service hears a recording, on ai-agents' host. */
|
|
4
|
+
export declare const TRANSCRIPTIONS_PATH = "/api/ai-agents/v1/speech/transcriptions";
|
|
5
|
+
/** The model the speech service hears with (agentspecs' catalogue). */
|
|
6
|
+
export declare const SERVER_HEARING_MODEL = "whisper-large-v3-turbo";
|
|
7
|
+
/** Where a chat's voice hears: in the browser, or on Datalayer's servers. */
|
|
8
|
+
export type HearingPlace = 'device' | 'server';
|
|
9
|
+
/** What decides where a chat's voice hears. */
|
|
10
|
+
export interface HearingAsked {
|
|
11
|
+
language: string;
|
|
12
|
+
/** The application's `interface.voice.where`; `auto` when unsaid. */
|
|
13
|
+
where?: 'auto' | 'device' | 'server';
|
|
14
|
+
/** The person asked for their language to be detected (VO-14). */
|
|
15
|
+
detect?: boolean;
|
|
16
|
+
/** The speech service; none, and everything is heard in the browser. */
|
|
17
|
+
speechUrl?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Where a chat's voice hears (§5): in the browser unless the speech service
|
|
21
|
+
* is named and the person asked for detection, the application says
|
|
22
|
+
* `server`, or it says `auto` and no model in the browser hears the language.
|
|
23
|
+
*/
|
|
24
|
+
export declare function hearingPlace(voice: HearingAsked): HearingPlace;
|
|
25
|
+
/** Mono samples at 16 kHz as a 16-bit PCM WAV file: what the service reads. */
|
|
26
|
+
export declare function wavOf(samples: Float32Array, rate?: number): ArrayBuffer;
|
|
27
|
+
/** What finds the speech in what was heard: the browser's VAD (`DeviceHearing`). */
|
|
28
|
+
export interface SpeechFinder {
|
|
29
|
+
readyToFindSpeech(progress?: HearingProgress): Promise<void>;
|
|
30
|
+
findSpeech(audio: Float32Array): Promise<{
|
|
31
|
+
speech: Float32Array;
|
|
32
|
+
silentMs: number;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
export interface ServerHearingOptions {
|
|
36
|
+
/** ai-agents' origin, where the speech service is. */
|
|
37
|
+
speechUrl: string;
|
|
38
|
+
/** The token the conversation speaks with: the person's, a visitor's, an embed's. */
|
|
39
|
+
token?: string;
|
|
40
|
+
/** Let Whisper find the language (VO-14), rather than tell it. */
|
|
41
|
+
detect?: boolean;
|
|
42
|
+
fetcher?: typeof fetch;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Hears on Datalayer's servers: the browser's VAD finds the speech and when
|
|
46
|
+
* it stopped; Whisper, on the speech service, writes it.
|
|
47
|
+
*/
|
|
48
|
+
export declare class ServerHearing implements Hearing {
|
|
49
|
+
private readonly finder;
|
|
50
|
+
private readonly options;
|
|
51
|
+
readonly where: "server";
|
|
52
|
+
constructor(finder: SpeechFinder, options: ServerHearingOptions);
|
|
53
|
+
/** Only the VAD loads in the page: the words are written on the server. */
|
|
54
|
+
ready(_language: string, progress?: HearingProgress): Promise<void>;
|
|
55
|
+
/** Whether speech was found and how long it has been silent; its words come at the end. */
|
|
56
|
+
hearSoFar(audio: Float32Array): Promise<HeardSoFar>;
|
|
57
|
+
hear(audio: Float32Array, language: string): Promise<Transcript | undefined>;
|
|
58
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Hearing on Datalayer's servers (VOICE.md VO-34, VO-14, §5): what was said
|
|
7
|
+
* in one press, kept to its speech by Silero VAD in the page, then sent as a
|
|
8
|
+
* WAV recording to the speech service's `POST /speech/transcriptions`, where
|
|
9
|
+
* Whisper large-v3-turbo hears it — in any language it knows, and finding
|
|
10
|
+
* the language itself when the person asks for that (detection, VO-14). The
|
|
11
|
+
* sound is held in the request only: the service keeps neither it nor the
|
|
12
|
+
* words (VO-60).
|
|
13
|
+
*
|
|
14
|
+
* Used where the application's voice says `where: server`, where it says
|
|
15
|
+
* `auto` and no model in the browser hears the language, and where the
|
|
16
|
+
* person asked for their language to be detected — never when they chose to
|
|
17
|
+
* keep speech on the device. A clip model: its words come at the end, not
|
|
18
|
+
* while the person speaks (VO-11).
|
|
19
|
+
*
|
|
20
|
+
* @module voice/serverHearing
|
|
21
|
+
*/
|
|
22
|
+
import { transcriberFor } from '../specs/voices';
|
|
23
|
+
import { HEARING_RATE } from './capture';
|
|
24
|
+
/** Where the speech service hears a recording, on ai-agents' host. */
|
|
25
|
+
export const TRANSCRIPTIONS_PATH = '/api/ai-agents/v1/speech/transcriptions';
|
|
26
|
+
/** The model the speech service hears with (agentspecs' catalogue). */
|
|
27
|
+
export const SERVER_HEARING_MODEL = 'whisper-large-v3-turbo';
|
|
28
|
+
/** The shortest speech worth hearing, in samples: a word said alone. */
|
|
29
|
+
const SHORTEST = HEARING_RATE * 0.2;
|
|
30
|
+
/**
|
|
31
|
+
* Where a chat's voice hears (§5): in the browser unless the speech service
|
|
32
|
+
* is named and the person asked for detection, the application says
|
|
33
|
+
* `server`, or it says `auto` and no model in the browser hears the language.
|
|
34
|
+
*/
|
|
35
|
+
export function hearingPlace(voice) {
|
|
36
|
+
if (!voice.speechUrl) {
|
|
37
|
+
return 'device';
|
|
38
|
+
}
|
|
39
|
+
if (voice.detect || voice.where === 'server') {
|
|
40
|
+
return 'server';
|
|
41
|
+
}
|
|
42
|
+
if (voice.where === 'device') {
|
|
43
|
+
return 'device';
|
|
44
|
+
}
|
|
45
|
+
return transcriberFor(voice.language) ? 'device' : 'server';
|
|
46
|
+
}
|
|
47
|
+
/** Mono samples at 16 kHz as a 16-bit PCM WAV file: what the service reads. */
|
|
48
|
+
export function wavOf(samples, rate = HEARING_RATE) {
|
|
49
|
+
const bytes = new ArrayBuffer(44 + samples.length * 2);
|
|
50
|
+
const view = new DataView(bytes);
|
|
51
|
+
const text = (at, value) => {
|
|
52
|
+
for (let i = 0; i < value.length; i++) {
|
|
53
|
+
view.setUint8(at + i, value.charCodeAt(i));
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
text(0, 'RIFF');
|
|
57
|
+
view.setUint32(4, 36 + samples.length * 2, true);
|
|
58
|
+
text(8, 'WAVE');
|
|
59
|
+
text(12, 'fmt ');
|
|
60
|
+
view.setUint32(16, 16, true);
|
|
61
|
+
view.setUint16(20, 1, true); // PCM
|
|
62
|
+
view.setUint16(22, 1, true); // mono
|
|
63
|
+
view.setUint32(24, rate, true);
|
|
64
|
+
view.setUint32(28, rate * 2, true);
|
|
65
|
+
view.setUint16(32, 2, true);
|
|
66
|
+
view.setUint16(34, 16, true);
|
|
67
|
+
text(36, 'data');
|
|
68
|
+
view.setUint32(40, samples.length * 2, true);
|
|
69
|
+
for (let i = 0; i < samples.length; i++) {
|
|
70
|
+
const sample = Math.max(-1, Math.min(1, samples[i]));
|
|
71
|
+
view.setInt16(44 + i * 2, sample < 0 ? sample * 0x8000 : sample * 0x7fff, true);
|
|
72
|
+
}
|
|
73
|
+
return bytes;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Hears on Datalayer's servers: the browser's VAD finds the speech and when
|
|
77
|
+
* it stopped; Whisper, on the speech service, writes it.
|
|
78
|
+
*/
|
|
79
|
+
export class ServerHearing {
|
|
80
|
+
finder;
|
|
81
|
+
options;
|
|
82
|
+
where = 'server';
|
|
83
|
+
constructor(finder, options) {
|
|
84
|
+
this.finder = finder;
|
|
85
|
+
this.options = options;
|
|
86
|
+
}
|
|
87
|
+
/** Only the VAD loads in the page: the words are written on the server. */
|
|
88
|
+
ready(_language, progress) {
|
|
89
|
+
return this.finder.readyToFindSpeech(progress);
|
|
90
|
+
}
|
|
91
|
+
/** Whether speech was found and how long it has been silent; its words come at the end. */
|
|
92
|
+
async hearSoFar(audio) {
|
|
93
|
+
const { speech, silentMs } = await this.finder.findSpeech(audio);
|
|
94
|
+
return { text: '', spoke: speech.length >= SHORTEST, silentMs };
|
|
95
|
+
}
|
|
96
|
+
async hear(audio, language) {
|
|
97
|
+
const started = performance.now();
|
|
98
|
+
const { speech } = await this.finder.findSpeech(audio);
|
|
99
|
+
if (speech.length < SHORTEST) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
const { speechUrl, token, detect, fetcher = fetch } = this.options;
|
|
103
|
+
const query = detect ? '' : `?language=${encodeURIComponent(language)}`;
|
|
104
|
+
const answered = await fetcher(`${speechUrl.replace(/\/+$/, '')}${TRANSCRIPTIONS_PATH}${query}`, {
|
|
105
|
+
method: 'POST',
|
|
106
|
+
headers: {
|
|
107
|
+
'Content-Type': 'audio/wav',
|
|
108
|
+
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
109
|
+
},
|
|
110
|
+
body: wavOf(speech),
|
|
111
|
+
});
|
|
112
|
+
const body = (await answered.json().catch(() => ({})));
|
|
113
|
+
if (!answered.ok) {
|
|
114
|
+
throw new Error(typeof body.detail === 'string' && body.detail
|
|
115
|
+
? body.detail
|
|
116
|
+
: `What you said could not be heard on Datalayer's servers (${answered.status}).`);
|
|
117
|
+
}
|
|
118
|
+
const text = typeof body.text === 'string' ? body.text.trim() : '';
|
|
119
|
+
if (!text) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
text,
|
|
124
|
+
metadata: {
|
|
125
|
+
input: 'voice',
|
|
126
|
+
language: typeof body.language === 'string' && body.language
|
|
127
|
+
? body.language
|
|
128
|
+
: language,
|
|
129
|
+
engine: SERVER_HEARING_MODEL,
|
|
130
|
+
where: 'server',
|
|
131
|
+
},
|
|
132
|
+
seconds: speech.length / HEARING_RATE,
|
|
133
|
+
ms: Math.round(performance.now() - started),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
package/lib/voice/settings.d.ts
CHANGED
|
@@ -57,6 +57,11 @@ export interface VoiceSettings {
|
|
|
57
57
|
* speech service, so answers are spoken only if the browser can do it.
|
|
58
58
|
*/
|
|
59
59
|
onDeviceOnly?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Let Whisper find the language they speak (VO-14): what they say is
|
|
62
|
+
* heard on Datalayer's servers, which detect it, rather than in the browser.
|
|
63
|
+
*/
|
|
64
|
+
detectLanguage?: boolean;
|
|
60
65
|
}
|
|
61
66
|
/** The settings as they can be used: numbers in range, nothing else trusted. */
|
|
62
67
|
export declare function saneVoiceSettings(settings: VoiceSettings | null | undefined): VoiceSettings;
|
package/lib/voice/settings.js
CHANGED
|
@@ -40,6 +40,9 @@ export function saneVoiceSettings(settings) {
|
|
|
40
40
|
if (typeof kept.onDeviceOnly === 'boolean') {
|
|
41
41
|
sane.onDeviceOnly = kept.onDeviceOnly;
|
|
42
42
|
}
|
|
43
|
+
if (typeof kept.detectLanguage === 'boolean') {
|
|
44
|
+
sane.detectLanguage = kept.detectLanguage;
|
|
45
|
+
}
|
|
43
46
|
return sane;
|
|
44
47
|
}
|
|
45
48
|
/** What was kept, or nothing. Storage may be refused, or hold nonsense. */
|
|
@@ -174,6 +177,9 @@ export function voiceForPerson(voice, person = {}, browser = browserLanguage())
|
|
|
174
177
|
if (chosen.microphoneId) {
|
|
175
178
|
next.microphoneId = chosen.microphoneId;
|
|
176
179
|
}
|
|
180
|
+
if (chosen.detectLanguage) {
|
|
181
|
+
next.detect = true;
|
|
182
|
+
}
|
|
177
183
|
if (chosen.onDeviceOnly) {
|
|
178
184
|
// Nothing is asked of the speech service: no token, no origin. Answers
|
|
179
185
|
// are said by that service only (decision 1: no voice made in the
|
|
@@ -181,6 +187,8 @@ export function voiceForPerson(voice, person = {}, browser = browserLanguage())
|
|
|
181
187
|
next.speechUrl = undefined;
|
|
182
188
|
next.token = undefined;
|
|
183
189
|
next.output = 'off';
|
|
190
|
+
next.where = 'device';
|
|
191
|
+
next.detect = false;
|
|
184
192
|
}
|
|
185
193
|
return next;
|
|
186
194
|
}
|
package/lib/voice/speaker.d.ts
CHANGED
|
@@ -24,10 +24,11 @@ export interface SpeakerOptions {
|
|
|
24
24
|
/** The voice of the catalogue. */
|
|
25
25
|
voice: string;
|
|
26
26
|
language: string;
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* The person's token, an application's principal's, a visitor's or an
|
|
29
|
+
* embed's — whose deployment is the one it names (VO-19, decision 16).
|
|
30
|
+
*/
|
|
28
31
|
token?: string;
|
|
29
|
-
/** The deployment an embed's page speaks for (VO-19): sent with each sentence. */
|
|
30
|
-
deployment?: string;
|
|
31
32
|
/**
|
|
32
33
|
* How fast it speaks, as a multiplier of the voice's own pace (VO-25,
|
|
33
34
|
* VO-30). Applied here rather than asked of the service: `POST
|
package/lib/voice/speaker.js
CHANGED
|
@@ -114,7 +114,7 @@ export class ServerSpeaker {
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
async fetch(text) {
|
|
117
|
-
const { speechUrl, voice, language, token,
|
|
117
|
+
const { speechUrl, voice, language, token, fetcher = fetch } = this.options;
|
|
118
118
|
try {
|
|
119
119
|
const answered = await fetcher(`${speechUrl.replace(/\/+$/, '')}${SYNTHESES_PATH}`, {
|
|
120
120
|
method: 'POST',
|
|
@@ -127,7 +127,6 @@ export class ServerSpeaker {
|
|
|
127
127
|
voice,
|
|
128
128
|
language,
|
|
129
129
|
format: 'opus',
|
|
130
|
-
...(deployment ? { deployment } : {}),
|
|
131
130
|
}),
|
|
132
131
|
});
|
|
133
132
|
if (!answered.ok) {
|
|
@@ -27,7 +27,6 @@ export function useVoiceSpeaker(voice, on = true) {
|
|
|
27
27
|
voice: voice.voice,
|
|
28
28
|
language: voice.language,
|
|
29
29
|
token: voice.token,
|
|
30
|
-
deployment: voice.deployment,
|
|
31
30
|
rate: voice.rate,
|
|
32
31
|
volume: voice.volume,
|
|
33
32
|
onState: setState,
|
|
@@ -40,7 +39,6 @@ export function useVoiceSpeaker(voice, on = true) {
|
|
|
40
39
|
voice: voice?.voice,
|
|
41
40
|
language: voice?.language,
|
|
42
41
|
token: voice?.token,
|
|
43
|
-
deployment: voice?.deployment,
|
|
44
42
|
rate: voice?.rate,
|
|
45
43
|
volume: voice?.volume,
|
|
46
44
|
});
|
|
@@ -49,7 +47,6 @@ export function useVoiceSpeaker(voice, on = true) {
|
|
|
49
47
|
voice?.voice,
|
|
50
48
|
voice?.language,
|
|
51
49
|
voice?.token,
|
|
52
|
-
voice?.deployment,
|
|
53
50
|
voice?.rate,
|
|
54
51
|
voice?.volume,
|
|
55
52
|
]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datalayer/agent-runtimes",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.30",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"workspaces": [
|
|
6
6
|
".",
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
"@datalayer/jupyter-react": "^2.0.20",
|
|
159
159
|
"@datalayer/jupyter-server-nbmodel": "^0.2.8",
|
|
160
160
|
"@datalayer/lexical-loro": "^1.0.6",
|
|
161
|
-
"@datalayer/primer-addons": "^1.0.
|
|
161
|
+
"@datalayer/primer-addons": "^1.0.40",
|
|
162
162
|
"@datalayer/primer-rjsf": "^1.0.3",
|
|
163
163
|
"@datalayer/personal-agent-protocol": "^0.24.0",
|
|
164
164
|
"@datalayer/reactor": "^1.3.0",
|
package/scripts/voice/measure.py
CHANGED
|
@@ -92,6 +92,67 @@ def transcribe(
|
|
|
92
92
|
return json.loads(done.stdout.strip().splitlines()[-1])
|
|
93
93
|
|
|
94
94
|
|
|
95
|
+
def measure_server(
|
|
96
|
+
store: Path,
|
|
97
|
+
voices: Any,
|
|
98
|
+
fixtures: List[Dict[str, Any]],
|
|
99
|
+
wavs: Dict[str, Path],
|
|
100
|
+
measured: Path,
|
|
101
|
+
threads: int,
|
|
102
|
+
) -> None:
|
|
103
|
+
"""Each server speech-to-text model on the fixtures, as the speech service runs it (VO-47).
|
|
104
|
+
|
|
105
|
+
faster-whisper, 8-bit on the CPU with ``threads`` threads, told the
|
|
106
|
+
language, one clip at a time: what it heard and how long it took.
|
|
107
|
+
"""
|
|
108
|
+
from faster_whisper import WhisperModel # the speech image's own
|
|
109
|
+
|
|
110
|
+
for model in voices.SPEECH_MODEL_CATALOGUE.values():
|
|
111
|
+
if model["task"] != "stt" or model["where"] != ["server"]:
|
|
112
|
+
continue
|
|
113
|
+
started = time.monotonic()
|
|
114
|
+
whisper = WhisperModel(
|
|
115
|
+
str(store / model["provider"] / model["id"]),
|
|
116
|
+
device="cpu",
|
|
117
|
+
compute_type="int8",
|
|
118
|
+
cpu_threads=threads,
|
|
119
|
+
)
|
|
120
|
+
load_ms = round((time.monotonic() - started) * 1000)
|
|
121
|
+
by_language = {}
|
|
122
|
+
for language in ("en", "fr"):
|
|
123
|
+
clips = [clip for clip in fixtures if clip["language"] == language]
|
|
124
|
+
print(f"{model['id']} on {len(clips)} {language} clips", flush=True)
|
|
125
|
+
results = []
|
|
126
|
+
for clip in clips:
|
|
127
|
+
samples, _ = sf.read(wavs[clip["id"]], dtype="float32")
|
|
128
|
+
started = time.monotonic()
|
|
129
|
+
parts, _ = whisper.transcribe(
|
|
130
|
+
samples, language=language, vad_filter=True
|
|
131
|
+
)
|
|
132
|
+
text = " ".join(part.text.strip() for part in parts).strip()
|
|
133
|
+
results.append(
|
|
134
|
+
{
|
|
135
|
+
"id": clip["id"],
|
|
136
|
+
"heard": text,
|
|
137
|
+
"ms": round((time.monotonic() - started) * 1000),
|
|
138
|
+
"audio_s": round(len(samples) / 16000, 2),
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
by_language[language] = {"load_ms": load_ms, "clips": results}
|
|
142
|
+
(measured / f"stt-{model['id']}.json").write_text(
|
|
143
|
+
json.dumps(
|
|
144
|
+
{
|
|
145
|
+
"model": model["id"],
|
|
146
|
+
"runtime": f"faster-whisper, int8 on the CPU, {threads} threads",
|
|
147
|
+
"languages": by_language,
|
|
148
|
+
},
|
|
149
|
+
ensure_ascii=False,
|
|
150
|
+
indent=2,
|
|
151
|
+
)
|
|
152
|
+
+ "\n"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
95
156
|
def main() -> int:
|
|
96
157
|
parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
|
|
97
158
|
parser.add_argument("--store", type=Path, required=True)
|
|
@@ -101,6 +162,14 @@ def main() -> int:
|
|
|
101
162
|
default=None,
|
|
102
163
|
help="Where @huggingface/transformers is installed, when not in this checkout",
|
|
103
164
|
)
|
|
165
|
+
parser.add_argument(
|
|
166
|
+
"--server",
|
|
167
|
+
action="store_true",
|
|
168
|
+
help="Measure the server's speech-to-text models instead, with faster-whisper (in the speech image)",
|
|
169
|
+
)
|
|
170
|
+
parser.add_argument(
|
|
171
|
+
"--threads", type=int, default=4, help="CPU threads for --server"
|
|
172
|
+
)
|
|
104
173
|
args = parser.parse_args()
|
|
105
174
|
voices = catalogue()
|
|
106
175
|
env = dict(os.environ)
|
|
@@ -124,6 +193,11 @@ def main() -> int:
|
|
|
124
193
|
wav = Path(scratch) / f"{clip['id']}.wav"
|
|
125
194
|
sf.write(wav, resample(samples, rate), 16000, subtype="PCM_16")
|
|
126
195
|
wavs[clip["id"]] = wav
|
|
196
|
+
if args.server:
|
|
197
|
+
measure_server(
|
|
198
|
+
args.store, voices, fixtures, wavs, measured, args.threads
|
|
199
|
+
)
|
|
200
|
+
return 0
|
|
127
201
|
# --- speech to text, on the device's models ---------------------------------
|
|
128
202
|
for model in voices.SPEECH_MODEL_CATALOGUE.values():
|
|
129
203
|
if model["task"] != "stt" or "device" not in model["where"]:
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import{E as N,e$ as xo,r as o,j as t,f0 as vo,f1 as So,b_ as Rn,B as p,H as bt,T as H,c as Y,cu as zt,eu as Dt,f2 as ce,co as xt,e as yo,f3 as Co,f4 as wo,e3 as ko,f5 as jo,aH as Io,dX as zn,dV as Eo,dY as Gt,f6 as To,f7 as Ro,f8 as Ao,f9 as Mo,fa as Po,fb as Uo,fc as $o,d3 as Fo,a_ as Lo,fd as Oo,fe as _o,ff as zo,fg as No,fh as Bo,fi as Vo,x as Nt,fj as Ho,fk as Wo,fl as Do,fm as Go,fn as Ko,fo as Yo,fp as qo,fq as Jo,fr as Qo,fs as Xo,ft as Zo,fu as es,fv as ts,fw as ns,fx as os,fy as ss,fz as as,fA as is,fB as rs,fC as ls,fD as tt,fE as cs,fF as ds,fG as us,fH as fs,fI as An,fJ as ps,fK as Le,fL as ms,fM as gs,a$ as vt,fN as hs,fO as bs,fP as Mn,fQ as xs,fR as vs,fS as Ss,fT as ys,eC as Cs,fU as Bt,fV as Ce,fW as ws,fX as ks,fY as js,fZ as Pn,f_ as Is,f$ as Un,g0 as Es,g1 as Ts,g2 as Rs,g3 as As,g4 as Ms,g5 as Ps,g6 as Us,g7 as $s,g8 as Fs,g9 as Ls,ga as St,gb as Os,gc as _s,gd as zs,I as Ns,ge as Bs,gf as Vs,gg as $n,gh as Hs,gi as Ws,gj as Ds,gk as Kt,gl as Gs,gm as Ks,ek as Vt,gn as Ys,go as qs}from"../datalayer-app-main.js";import{S as Js,E as Qs,a as Xs}from"./SignInIcon-4HkE48mS.js";import{F as Zs,a as ea,b as ta}from"./LinkedInGreyIcon-BoWXE-HS.js";function Nn(){const n=N(xo);return o.useMemo(()=>n.map(a=>a.value),[n])}const Ht={github:{name:"github",oauth2CallbackServerRoute:"jupyter_iam/oauth2/callback",oauth2CallbackUIRoute:"/iam/oauth2/github/callback"},google:{name:"google",oauth2CallbackServerRoute:"jupyter_iam/oauth2/callback",oauth2CallbackUIRoute:"/iam/oauth2/google/callback"},linkedin:{name:"linkedin",oauth2CallbackServerRoute:"jupyter_iam/oauth2/callback",oauth2CallbackUIRoute:"/iam/oauth2/linkedin/callback"}},Fn=()=>{if(typeof document>"u")return"";const n=document.getElementById("datalayer-config-data");if(!n?.textContent)return"";try{const a=JSON.parse(n.textContent);return String(a?.iamUrl||"").replace(/\/$/,"")}catch{return""}},na=(n,a="Unable to start social sign-in.")=>{if(n instanceof Error&&n.message)return n.message;if(typeof n=="string"&&n.trim())return n;if(n&&typeof n=="object"&&"message"in n){const l=n.message;if(typeof l=="string"&&l.trim())return l}return a},oa=jo`
|
|
2
|
-
.signin-input-theme-scope input:-webkit-autofill,
|
|
3
|
-
.signin-input-theme-scope input:-webkit-autofill:hover,
|
|
4
|
-
.signin-input-theme-scope input:-webkit-autofill:focus,
|
|
5
|
-
.signin-input-theme-scope input:-webkit-autofill:active,
|
|
6
|
-
.signin-input-theme-scope input:-webkit-autofill:disabled,
|
|
7
|
-
.signin-input-theme-scope textarea:-webkit-autofill,
|
|
8
|
-
.signin-input-theme-scope textarea:-webkit-autofill:hover,
|
|
9
|
-
.signin-input-theme-scope textarea:-webkit-autofill:focus,
|
|
10
|
-
.signin-input-theme-scope textarea:-webkit-autofill:active,
|
|
11
|
-
.signin-input-theme-scope textarea:-webkit-autofill:disabled {
|
|
12
|
-
-webkit-text-fill-color: var(--fgColor-default) !important;
|
|
13
|
-
caret-color: var(--fgColor-default) !important;
|
|
14
|
-
-webkit-box-shadow: 0 0 0 1000px var(--bgColor-default) inset !important;
|
|
15
|
-
box-shadow: 0 0 0 1000px var(--bgColor-default) inset !important;
|
|
16
|
-
background-color: var(--bgColor-default) !important;
|
|
17
|
-
transition: background-color 9999s ease-in-out 0s !important;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
.signin-input-theme-scope input:disabled,
|
|
21
|
-
.signin-input-theme-scope textarea:disabled {
|
|
22
|
-
-webkit-text-fill-color: var(--fgColor-muted) !important;
|
|
23
|
-
-webkit-box-shadow: 0 0 0 1000px var(--bgColor-default) inset !important;
|
|
24
|
-
box-shadow: 0 0 0 1000px var(--bgColor-default) inset !important;
|
|
25
|
-
background-color: var(--bgColor-default) !important;
|
|
26
|
-
opacity: 1 !important;
|
|
27
|
-
}
|
|
28
|
-
`,sa=({onSignIn:n,onApiKeySignIn:a,loginUrl:l,name:r,title:f="Datalayer OTEL",description:x="Sign in to access the observability dashboard.",icon:y,leadingIcon:I=t.jsx(ko,{size:24}),github:d=!1,google:we=!1,linkedin:q=!1,apiKey:nt=!1,signUp:F=!0,socialSignInNavigationTarget:ee,asDoc:v=!1,fillHeight:E=!0,hideHero:te=!1,calloutTitle:Oe,calloutDescription:ot,passwordToggleLabel:de="Sign in with a password",signUpTitle:Yt="Don't have an account?",signUpDescription:J="Create a free Datalayer account with your email address.",signUpLabel:yt="Sign up with email",signUpHref:_e="https://datalayer.ai/signup",onSignUp:B,forgotPasswordHref:Q="/password",forgotPasswordLabel:ze="Forgot password?",showForgotPassword:Ct=!0,socialButtonMarginTop:h=3,actionButtonMaxWidth:X=320})=>{const ne=v&&!te,st=r??(v&&f==="Datalayer OTEL"?"Datalayer Sign In":f),wt=y??(v?t.jsx(Js,{size:24}):I),L=o.useMemo(()=>{if(l)return l;const c=Fn();return c?`${c}/api/iam/v1/login`:"/api/iam/v1/login"},[l]),[W,ke]=o.useState(""),[ue,kt]=o.useState(""),[at,jt]=o.useState(!1),[je,fe]=o.useState(!1),[w,pe]=o.useState(!1),[oe,it]=o.useState(!1),[Ne,Ie]=o.useState(null),[O,It]=o.useState(null),[me,Ee]=o.useState(""),[Te,rt]=o.useState(null),[Re,$]=o.useState(null),{enqueueToast:ge}=vo(),[Ae,se]=o.useState(!1),[ae,he]=o.useState(""),j=o.useRef(null),Me=o.useCallback(c=>So()?Rn.URLExt.join(Rn.PageConfig.getBaseUrl(),c.oauth2CallbackServerRoute):`${window.location.protocol}//${window.location.hostname}${window.location.port?`:${window.location.port}`:""}${c.oauth2CallbackUIRoute}`,[]),D=o.useMemo(()=>{if(typeof window>"u")return"/";const c=`${window.location.pathname}${window.location.search}${window.location.hash}`;return c.startsWith("/")?c:"/"},[]),V=o.useCallback(async c=>{if(!oe){$(null),Ie(c.name),it(!0);try{const k=Fn(),P=new URLSearchParams({provider:c.name,callback_uri:Me(c)}),g=ee===void 0?void 0:String(ee||"").trim(),R=g===void 0?D&&D!=="/"?D:void 0:g&&g.startsWith("/")&&!g.startsWith("//")?g:void 0;R&&P.set("post_auth_redirect",R);const re=`${k||""}/api/iam/v1/oauth2/authz/url?${P.toString()}`,S=await fetch(re),De=await S.json(),Pe=De?.autorization_url;if(!S.ok||!Pe)throw new Error(De?.message||"Unable to start social sign-in.");window.location.assign(Pe)}catch(k){const P=na(k);$(P),ge(P,{variant:"error"}),Ie(null),it(!1)}}},[Me,D,ge,oe,ee]);o.useEffect(()=>{Ae&&j.current?.focus()},[Ae]);const ie=o.useCallback(()=>{se(!1),he("")},[]),lt=o.useCallback(()=>{!ae.trim()||!a||(a(ae.trim()),ie())},[ae,a,ie]),ct=o.useCallback(()=>{if(B){B();return}window.location.assign(_e)},[B,_e]),G=o.useCallback(()=>{if(Q){if(Q.startsWith("http://")||Q.startsWith("https://")){window.open(Q,"_blank","noopener,noreferrer");return}window.location.assign(Q)}},[Q]),Be=o.useCallback(async()=>{if(!(v||!W||!ue||w)){pe(!0),$(null);try{const c=await fetch(L,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({handle:W,password:ue})});if(!c.ok)throw new Error(`HTTP ${c.status}`);const k=await c.json();if(k.success&&k.token){const P=String(k?.user?.id||k?.user?.uid||"").trim();if(!!k?.user?.mfaUrl&&P){It(P),rt(k.token),$(null);return}n&&n(k.token,W)}else $(k.message||"Invalid username or password.")}catch(c){$(c instanceof Error?c.message:String(c))}finally{pe(!1)}}},[v,W,ue,w,L,n]),Ve=o.useCallback(async()=>{if(!(v||w||!O||!me.trim()||!Te)){pe(!0),$(null);try{const c=new URL(L,window.location.origin),k=c.pathname.endsWith("/api/iam/v1/login")?c.href.replace(/\/api\/iam\/v1\/login$/,""):`${c.origin}`,P=await fetch(`${k}/api/iam/v1/users/${O}/mfa/validate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({userUid:O,code:me.trim()})}),g=await P.json();if(!P.ok||!g?.success)throw new Error(g?.message||"Failed to validate your MFA code.");n&&n(Te,W)}catch(c){const k=c instanceof Error?c.message:String(c);$(k),ge(k,{variant:"error"})}finally{pe(!1)}}},[v,ge,W,w,L,me,Te,O,n]),He=o.useCallback(c=>{c.key==="Enter"&&(O?Ve():Be())},[O,Be,Ve]),We=oe||w||v;return t.jsxs(p,{className:"signin-input-theme-scope",display:"flex",alignItems:te||ne||!E?"flex-start":"center",justifyContent:"center",minHeight:te||ne||!E?"auto":"100vh",bg:te?"transparent":"canvas.default",color:"fg.default",py:te?0:ne?2:4,children:[t.jsx(oa,{}),t.jsxs(p,{width:"100%",maxWidth:440,p:0,children:[!te&&t.jsxs(t.Fragment,{children:[t.jsxs(p,{display:"flex",alignItems:"center",gap:2,mb:ne?2:3,justifyContent:"center",children:[wt,t.jsx(bt,{sx:{fontSize:3},children:st})]}),t.jsx(H,{as:"p",sx:{fontSize:1,color:"fg.muted",mb:ne?2:3,textAlign:"center"},children:x})]}),(Oe||ot)&&t.jsxs(p,{mb:3,textAlign:"center",children:[Oe&&t.jsx(bt,{as:"h3",sx:{fontSize:2,mb:1},children:Oe}),ot&&t.jsx(H,{sx:{color:"fg.muted",fontSize:1},children:ot})]}),!O&&(d||we||q)&&t.jsxs(t.Fragment,{children:[d&&t.jsx(Y,{block:!0,size:"large",leadingVisual:Ne==="github"?()=>t.jsx(zt,{size:"small"}):Zs,disabled:We,onClick:()=>V(Ht.github),sx:{mt:h,maxWidth:X,mx:"auto"},children:"Sign in with GitHub"}),we&&t.jsx(Y,{block:!0,size:"large",leadingVisual:Ne==="google"?()=>t.jsx(zt,{size:"small"}):ea,disabled:We,onClick:()=>V(Ht.google),sx:{mt:h,maxWidth:X,mx:"auto"},children:"Sign in with Google"}),q&&t.jsx(Y,{block:!0,size:"large",leadingVisual:Ne==="linkedin"?()=>t.jsx(zt,{size:"small"}):ta,disabled:We,onClick:()=>V(Ht.linkedin),sx:{mt:h,maxWidth:X,mx:"auto"},children:"Sign in with LinkedIn"})]}),!O&&!at&&t.jsx(p,{mt:3,display:"flex",justifyContent:"center",children:t.jsx(Y,{leadingVisual:Dt,size:"large",block:!0,disabled:v||w||oe,onClick:()=>jt(!0),sx:{maxWidth:X,mx:"auto"},children:de})}),!O&&at&&t.jsxs(p,{mt:4,children:[t.jsxs(ce,{required:!0,sx:{mb:3},children:[t.jsx(ce.Label,{children:"Username"}),t.jsx(xt,{autoFocus:!0,block:!0,placeholder:"Your username",value:W,disabled:w||v,onChange:c=>ke(c.target.value),onKeyDown:He})]}),t.jsxs(ce,{required:!0,sx:{mb:3},children:[t.jsx(ce.Label,{children:"Password"}),t.jsx(xt,{block:!0,placeholder:"Your password",type:je?"text":"password",value:ue,disabled:w||v,onChange:c=>kt(c.target.value),onKeyDown:He,trailingAction:t.jsx(xt.Action,{onClick:()=>fe(!je),disabled:w||v,icon:je?Qs:Xs,"aria-label":je?"Hide password":"Show password",sx:{color:"var(--fgColor-muted)"}})})]}),t.jsxs(p,{display:"flex",alignItems:"center",justifyContent:"space-between",mt:3,children:[t.jsx(Y,{variant:"primary",disabled:w||v||!W||!ue,onClick:Be,children:w?"Signing in…":"Sign In"}),Ct&&t.jsx(yo,{as:"button",sx:{fontSize:1,cursor:v?"not-allowed":"pointer",background:"none",border:"none",opacity:v?.6:1},onClick:()=>{v||w||G()},children:ze})]})]}),O&&t.jsxs(t.Fragment,{children:[t.jsxs(ce,{required:!0,sx:{mb:3,mt:2},children:[t.jsx(ce.Label,{children:"MFA Code"}),t.jsx(xt,{autoFocus:!0,block:!0,placeholder:"Your MFA code",value:me,disabled:w||v,onChange:c=>Ee(c.target.value),onKeyDown:He})]}),t.jsx(Y,{variant:"primary",block:!0,disabled:w||v||!me.trim(),onClick:Ve,sx:{maxWidth:X,mx:"auto"},children:w?"Validating…":"Validate MFA Code"})]}),Re&&t.jsx(H,{sx:{color:"danger.fg",fontSize:1,mt:3,display:"block"},children:Re}),F&&t.jsxs(p,{mt:4,pt:4,textAlign:"center",borderTop:"1px solid",borderColor:"border.muted",children:[t.jsx(bt,{as:"h3",sx:{fontSize:2,mb:2},children:Yt}),t.jsx(H,{as:"p",sx:{color:"fg.muted",fontSize:1,mb:3},children:J}),t.jsx(p,{display:"flex",justifyContent:"center",children:t.jsx(Y,{variant:"primary",size:"large",leadingVisual:Co,disabled:v||w,onClick:ct,children:yt})})]}),nt&&a&&t.jsxs(t.Fragment,{children:[t.jsxs(p,{display:"flex",alignItems:"center",gap:2,my:3,children:[t.jsx(p,{flex:1,height:"1px",bg:"border.default"}),t.jsx(H,{sx:{fontSize:0,color:"fg.muted"},children:"or"}),t.jsx(p,{flex:1,height:"1px",bg:"border.default"})]}),t.jsx(Y,{block:!0,leadingVisual:Dt,disabled:v||w||oe,onClick:()=>se(!0),sx:{maxWidth:X,mx:"auto"},children:"Sign in with an API Key"}),Ae&&t.jsx(p,{role:"dialog","aria-modal":"true","aria-labelledby":"signin-api-key-title",onKeyDown:c=>{c.key==="Escape"&&ie()},onClick:ie,position:"fixed",inset:0,zIndex:200,p:3,bg:"canvas.backdrop",display:"flex",alignItems:"center",justifyContent:"center",children:t.jsxs(p,{as:"form",onClick:c=>c.stopPropagation(),onSubmit:c=>{c.preventDefault(),lt()},width:"min(560px, 100%)",p:3,borderRadius:2,border:"1px solid",borderColor:"border.default",bg:"canvas.overlay",color:"fg.default",boxShadow:"shadow.large",children:[t.jsx(bt,{id:"signin-api-key-title",sx:{fontSize:2,mb:2},children:"Enter your API Key"}),t.jsxs(ce,{required:!0,children:[t.jsx(ce.Label,{children:"API Key"}),t.jsx(wo,{block:!0,required:!0,autoFocus:!0,placeholder:"Paste your API key here",value:ae,onInput:c=>he(c.currentTarget.value),ref:j})]}),t.jsxs(p,{mt:3,display:"flex",justifyContent:"flex-end",gap:2,children:[t.jsx(Y,{type:"button",onClick:ie,children:"Cancel"}),t.jsx(Y,{type:"submit",variant:"primary",disabled:!ae.trim(),children:"Authenticate"})]})]})})]})]})]})};function aa(n){const{agentId:a,instructions:l,model:r,frontendTools:f,inference:x,subagents:y,sharing:I,onSubagentEvent:d,modeEffect:we}=n;return{type:"browser-vercel-ai",endpoint:"",agentId:a,enableConfigQuery:!1,options:{instructions:l,model:r,frontendTools:f,inference:x,subagents:y,sharing:I,onSubagentEvent:d,modeEffect:we}}}const Ln=Io((n,a)=>({pending:void 0,suggest:(l,r)=>{const f=l.trim();f&&n({pending:{text:f,submit:r?.submit??!1,nonce:(a().pending?.nonce??0)+1}})},consume:()=>n({pending:void 0})}));function ia({agentName:n,sandboxStillRuns:a=!1,temporary:l=!0,onSignedIn:r,onSignUp:f}){const{configuration:x}=zn(),y=o.useCallback(I=>{Eo.getState().checkIAMToken(I),r?.()},[r]);return t.jsx(p,{height:"100%",minHeight:0,overflowY:"auto",bg:"canvas.default",px:4,py:4,children:t.jsxs(p,{maxWidth:440,mx:"auto",children:[t.jsxs(p,{display:"flex",alignItems:"center",justifyContent:"center",gap:2,color:"attention.fg",mb:2,children:[t.jsx(Dt,{size:20}),t.jsx(H,{sx:{fontSize:2,fontWeight:"semibold",color:"fg.default"},children:l?"This demo runs on a shared key":"Your key has expired"})]}),t.jsxs(H,{as:"p",sx:{fontSize:1,color:"fg.muted",textAlign:"center",mb:3},children:[l?`${n??"The agent"} has used the time this key allows. Sign in to point your own agent at it, use your own data, and drop the time limit.`:`${n??"The agent"} stopped because your session ran out.`,a?" Your notebook is still running in this page.":""]}),t.jsx(sa,{hideHero:!0,fillHeight:!1,calloutTitle:"Sign in to keep going",github:!0,google:!0,linkedin:!0,socialSignInNavigationTarget:typeof window>"u"?void 0:`${window.location.pathname}${window.location.search}`,loginUrl:x?.iamUrl?`${x.iamUrl}/api/iam/v1/login`:void 0,onSignIn:y,onSignUp:f})]})})}const ra="https://r1.datalayer.run";function la(n=!0){const{configuration:a}=zn(),l=Gt(I=>I.token),r=a?.aiInferenceUrl||ra,f=To(r,n&&!l),x=l||f.token,y=o.useMemo(()=>({inferenceUrl:r,token:x||void 0}),[r,x]);return{inference:y,needsSignIn:Ro(y),anonymous:f,inferenceUrl:r}}const ca={name:"general-purpose",description:"A capable generalist for a task none of the specialists covers: research, reasoning, drafting, checking.",instructions:"You are a capable general-purpose assistant. Carry out the task you are given completely and report the result plainly, saying what you could not do."};function On(n){const a=n?.subagents;if(!a)return[];const l=(a.subagents??[]).map(r=>{const f=r.ref?Ao(r.ref):void 0;return{name:r.name,description:r.description||f?.description||r.name,instructions:r.instructions??f?.systemPrompt,model:r.model??f?.model??a.defaultModel,icon:f?.icon}});return a.includeGeneralPurpose?[...l,ca]:l}function da(n){return n.replace(/^datalayer_/,"").replace(/_lexical$/,"")}function ua(n){const a=da(n);return/^executeCodeIn(Notebook|Document)$/.test(a)||/^(read|list)[A-Z]/.test(a)}function fa(n,a){return n.filter(l=>a?.has(l.name)||ua(l.name))}function pa(n,a){const l=r=>a&&r.value.id===`${a}-tools`?0:r.value.id==="notebook-tools"?1:2;return n.map((r,f)=>({entry:r,index:f,rank:l(r)})).sort((r,f)=>r.rank-f.rank||r.index-f.index).map(r=>r.entry)}function ma(n,a,l){if(!l||!n.endsWith("-tools"))return!1;const r=n.slice(0,-6);return a.has(r)&&r!==l}const ga={type:"object",properties:{}};function ha(n,a){const l=new Set(n.toolset);return n.commands.filter(r=>l.has(r.name)).map(r=>({name:r.name,description:r.description,parameters:r.parameters??ga,location:"frontend",handler:async f=>{if(!a)throw new Error(`${r.name}: the ${n.plugin??n.id} plugin is not mounted on this page.`);const x=Object.keys(f??{}).length>0?f:void 0,y=await a.executeCommand(r.command,x);return y===void 0?{ok:!0,command:r.command,argument:x}:y}}))}function Bn(n,a){return n.flatMap(l=>ha(l,a))}function ba(){const n=Mo(),a=Nn();return o.useMemo(()=>Bn(a,n),[a,n])}function xa(n){const a=n?.app_instance;if(!a||typeof a!="object")return"";const l=a.deployment_uid;return typeof l=="string"?l.trim():""}function va(n,a,l){return n||!a?n:{id:l,messages:[]}}const Sa=({state:n})=>Po[n],ya="var(--theme-accent)";function Vn({face:n,size:a}){return typeof n=="string"?t.jsx(Uo,{emoji:n,size:a,label:""}):t.jsx(t.Fragment,{children:n})}function Ca({face:n,size:a,state:l}){const r=l==="thinking"||l==="working",f=l==="paused";return t.jsxs(p,{as:"span","aria-hidden":!0,"data-presence":l,position:"relative",display:"inline-flex",alignItems:"center",justifyContent:"center",width:a+8,height:a+8,flexShrink:0,fontSize:a,lineHeight:1,children:[t.jsx(p,{as:"span",position:"absolute",inset:0,borderRadius:"50%",boxShadow:f?"none":`0 0 0 2px ${ya}`,border:f?"2px dashed":"none",borderColor:"border.default",opacity:r||l==="waiting"||f?.9:0,transition:"opacity var(--theme-motion-status, 0ms) var(--theme-motion-easing, ease)",animation:r?"loopPresenceBreath 1.8s ease-in-out infinite":"none",reducedMotion:{animation:"none"},sx:{"@keyframes loopPresenceBreath":{"0%, 100%":{transform:"scale(0.92)",opacity:.35},"50%":{transform:"scale(1.06)",opacity:.9}}}}),t.jsx(p,{as:"span",display:"inline-flex",opacity:f?.55:1,filter:f?"grayscale(0.6)":"none",transition:"opacity var(--theme-motion-status, 0ms) var(--theme-motion-easing, ease)",children:t.jsx(Vn,{face:n,size:a})})]})}function wa({state:n}){return t.jsx(H,{role:"status","aria-live":"polite","data-presence-line":n,sx:{fontSize:0,color:n==="waiting"?"fg.default":"fg.muted",fontWeight:n==="waiting"?"semibold":"normal",whiteSpace:"nowrap",flexShrink:0},children:Sa({state:n})})}const ka=Kt(void 0),ja=Kt(""),Ia=Kt({}),Z="",Ea=1400,_n=parseInt(Ys["--loop-face-large"],10);function Pa({workspace:n}){const a=$o(),l=Fo(),r=o.useRef(null),{setViewControls:f}=n,[x,y]=o.useState(null),I=Lo(),d=I.getConfig(Oo),we=d?.placeholder||a.placeholder,q=d?.defaultSurface??"notebook",nt=I.getConfig(_o),[F,ee]=o.useState(Z),v=o.useRef(!1),E=N(zo),te=N(No),Oe=N(Bo),de=N(Vo)[0]?.value,J=Nt(de?.promptStance??ka)??de?.prompt,yt=N(Ho),_e=Wo(yt[0]?.value),B=o.useRef(_e);B.current=_e;const Q=o.useRef(""),ze=N(Do),Ct=o.useMemo(()=>Go(()=>ze.reduce((e,i)=>({...e,...i.value.extras.value}),{})),[ze]),h=Nt(ze.length>0?Ct:Ia),X=o.useRef(new Set),ne=Ko(Gs,n),st=!ne.allowed,wt=st?ne.reason??"Chat is unavailable for this sandbox":void 0,[L,W]=o.useState(!1),[ke,ue]=o.useState(!1),[kt,at]=o.useState(!1),[jt,je]=o.useState({open:!1,pendingApproval:!1}),fe=Yo(L,jt,d?.presence?.paused===!0),w=d?.presence?.onPresence;o.useEffect(()=>{w?.(fe),B.current?.setPresence(fe)},[w,fe]);const pe=o.useRef(d?.presence?.onSaying);pe.current=d?.presence?.onSaying;const oe=o.useRef({answering:!1}),it=o.useCallback(e=>{r.current=e,ue(!!e)},[]),Ne=o.useCallback(e=>{const{open:i,pendingApproval:s}=qo(e);je(ye=>ye.open===i&&ye.pendingApproval===s?ye:{open:i,pendingApproval:s});const m={saying:Qo(e),answering:Jo(e)},u=oe.current;(u.saying?.id!==m.saying?.id||u.saying?.text!==m.saying?.text||u.answering!==m.answering)&&(oe.current=m,pe.current?.(m));const z=B.current;if(!z)return;z.items(e);const M=new Set(["complete","completed","done","error","failed","cancelled","canceled","rejected","denied","stopped"]),K={insertcell:"adding a cell",updatecell:"editing a cell",runcell:"running a cell",deletecells:"removing cells",readallcells:"reading the notebook",readcell:"reading a cell",executecode:"running code",insertblock:"adding a block",updateblock:"editing a block",runblock:"running a block",readdocument:"reading the document"};let b;for(let ye=e.length-1;ye>=0;ye-=1){const ht=e[ye];if(typeof ht.toolName=="string"&&ht.toolCallId){M.has(String(ht.status).toLowerCase())||(b=ht);break}}if(!b){z.activity(void 0);return}const Se=b.toolName.toLowerCase().replace(/^datalayer_/,"").replace(/_lexical$/,"").replace(/[^a-z]/g,""),bo=K[Se]??`using ${b.toolName}`;z.activity(`${Q.current||"The agent"} is ${bo}…`)},[]),Ie=o.useRef(h);Ie.current=h;const O=o.useCallback(e=>{eo(e),B.current?.usage(e),Ie.current.onUsage?.(e)},[]),It=o.useCallback(e=>{W(e),e||B.current?.end("done")},[]),me=o.useCallback(e=>{const i=B.current;if(!i)return;let s=-1;for(let u=e.length-1;u>=0;u-=1)if(e[u].role==="user"){s=u;break}const m=e.slice(s+1).filter(u=>u.role==="assistant").map(Xo).filter(u=>u.trim().length>0).join(`
|
|
29
|
-
|
|
30
|
-
`);i.assistant(m)},[]),Ee=o.useRef([]),Te=o.useRef(!1),rt=o.useRef(void 0),Re=o.useRef(void 0),$=o.useRef(void 0),ge=N(Zo),Ae=o.useRef(ge);Ae.current=ge;const se=o.useCallback((e,i)=>{if(Re.current)return Re.current;const s=$.current;$.current=void 0;const m=es(Ae.current.map(M=>M.value),s?{...i??{},voice:s}:i),u=m?.loop;if(Te.current&&u?.files&&u.files.length>0){const M=rt.current;if(!M)return"A file is given to an application running on a runtime: run it on Datalayer or on your machine to give it one.";let K;try{K=u.files.map(b=>({file:ns(String(b.name??"")),base64:ts(String(b.data_url??""))}))}catch(b){return b instanceof Error?b.message:String(b)}M.execute(os(K)).then(b=>{if(!b.success){y(`The files could not be put in the sandbox: ${b.error??"it failed"}.`);return}se(`${e}
|
|
31
|
-
|
|
32
|
-
${ss(K.map(Se=>Se.file))}`,{...m,loop:Object.fromEntries(Object.entries(u).filter(([Se])=>Se!=="files"))})});return}B.current?.begin(e,r.current?.thread());const z=r.current?.send;if(!z){Ee.current.push([e,m]);return}z(e,m)},[]);o.useEffect(()=>{f(ke?{busy:L,stop:r.current?.stop,newChat:r.current?.newChat,send:se}:null)},[L,ke,f,se]),o.useEffect(()=>()=>f(null),[f]),o.useEffect(()=>n.prompts.subscribe(se),[n.prompts,se]),o.useEffect(()=>{if(!ke||Ee.current.length===0)return;const e=Ee.current.splice(0,Ee.current.length);for(const[i,s]of e)r.current?.send(i,s)},[ke]);const ae=o.useRef(!1),he=o.useCallback(async e=>{y(null);const i=await as(Ie.current,e);if(i){i.sent?Je(""):y(i.refused);return}ae.current||(ae.current=!0,nt?.firstPromptHook?.());const s=await n.submit(e);if(!s.handled){y(s.reason??null);return}Je(""),s.result?.content&&y(s.result.content)},[n,nt?.firstPromptHook]),j=o.useMemo(()=>E.find(e=>e.value.surfaceId===F)?.value,[E,F]),Me=o.useCallback(e=>{v.current=!0,ee(e)},[]);o.useEffect(()=>is(Me),[Me]),o.useEffect(()=>{rs(E.map(e=>e.value.surfaceId))},[E]),o.useEffect(()=>{ls(F&&F!==Z?F:qs)},[F]);const D=o.useMemo(()=>q===Z||q==="none"?void 0:E.find(e=>e.value.surfaceId===q),[E,q]);o.useEffect(()=>{v.current||!D||tt(D.value,n)&&ee(q)},[D,n,q]);const[V,ie]=o.useState(null),lt=o.useRef(null),ct=d?.fullScreenTopOffset??0,{fullScreen:G,usingApi:Be,toggle:Ve,topOffsetPx:He}=cs(lt,{forceOverlay:!!ct,topOffset:ct}),[We,c]=o.useState(!1),k=o.useRef(!1);o.useEffect(()=>{!L||k.current||G||(k.current=!0,c(!0))},[L,G]),o.useEffect(()=>{G&&c(!1)},[G]),o.useEffect(()=>{j&&!tt(j,n)&&(ie(j.surfaceId),ee(Z))},[j,n]);const P=o.useMemo(()=>{if(V)return E.find(e=>e.value.surfaceId===V)?.value;if(!v.current&&F===Z)return D?.value},[E,V,F,D]);o.useEffect(()=>{if(!V||F!==Z)return;const e=E.find(i=>i.value.surfaceId===V);e&&tt(e.value,n)&&(ie(null),ee(V))},[E,F,V,n]);const g=n.agentId?.trim()||"default",R=ds(n.sandbox.target??"local");Te.current=R;const C=us(),re=Nt(C?.selected??ja),S=C?.members.find(e=>e.id===re),De=N(fs)[0]?.value,Pe=De?.specId,A=An(S?.specId??g)??(Pe?An(Pe):void 0),Ge=S?void 0:De,qt=R&&!S?Ds(Pe,A):void 0;Q.current=S?.name??A?.name??g;const Jt=N(ps),Qt=h.hiddenOpeners,Xt=h.hideOpeners,Et=h.openers,Tt=o.useMemo(()=>{if(Xt)return[];const e=new Set(Qt??[]),i=b=>e.size===0?b:b.filter(Se=>!e.has(Se.text));if(Et)return i(Et);const s=Jt.flatMap(b=>b.value.suggestions);if(s.length>0)return i(s);const m=i(A?.suggestions??[]),u=i(C?.team.suggestions??[]);if(u.length===0)return m;const z=new Set(u.map(b=>b.text)),M=C?.team.name??"Team",K=S?.name??A?.name??g;return[...u.map(b=>({...b,group:M})),...m.filter(b=>!z.has(b.text)).map(b=>({...b,group:K}))]},[Jt,C,A,S,g,Qt,Xt,Et]),dt=o.useMemo(()=>Tt.map(e=>({title:e.summary??e.text,message:e.message??e.text,group:e.group})),[Tt]),Rt=Le(A?.icon),_=d?.presence,Hn=Le(C?.team.icon),Zt=N(ms),ut=o.useMemo(()=>{const e=[],i=new Map,s=new Set,m=new Set(E.map(u=>u.value.surfaceId));for(const u of pa(Zt,j?.surfaceId)){if(ma(u.value.id,m,j?.surfaceId))continue;let z;try{z=u.value.tools(n)}catch(M){console.warn(`[loop] frontend tools from '${u.plugin}' failed:`,M);continue}for(const M of z){const K=i.get(M.name);if(K){console.warn(`[loop] frontend tool '${M.name}' from '${u.plugin}' is shadowed by the one from '${K}'.`);continue}i.set(M.name,u.plugin),e.push(M),u.value.chatView&&s.add(M.name)}}for(const u of h.frontendTools??[])i.has(u.name)||(i.set(u.name,"chat-extras"),e.push(u),s.add(u.name));return j?e:fa(e,s)},[Zt,n.surfaceId,n.agentId,h.frontendTools,j?.surfaceId,E]),At=ba(),Ke=o.useMemo(()=>{if(At.length===0)return ut;const e=new Set(ut.map(i=>i.name));return[...ut,...At.filter(i=>!e.has(i.name))]},[ut,At]),en=Nn(),Mt=h.agentToolBundles,Wn=o.useMemo(()=>{const e=h.frontendTools??[];if(!Mt?.length)return h.frontendTools;const i=Bn(en.filter(m=>Mt.includes(m.id)),I),s=new Set(e.map(m=>m.name));return[...e,...i.filter(m=>!s.has(m.name))]},[h.frontendTools,Mt,en,I]);rt.current=R&&Ke.some(e=>e.name===gs)?I.getOutput(vt)?.sandbox:void 0;const T=hs(n.sandbox,n.serverUrl),tn=N(bs),Dn=o.useMemo(()=>tn.filter(e=>e.value.composer).map(e=>({name:e.value.name,description:e.value.description})),[tn]),Gn=o.useMemo(()=>{if(!C||!S)return On(A).map(s=>({name:s.name,description:s.description,icon:Le(s.icon)}));const e=C.members.map(s=>({name:s.name,description:s.description,emoji:s.emoji,icon:Le(s.icon),disabled:s.id===S.id,disabledReason:s.id===S.id?`You are already talking to ${s.name}`:void 0})),i=Mn(C.team,S.id).filter(s=>!C.members.some(m=>m.name===s.name)).map(s=>({name:s.name,description:s.description,icon:Le(s.icon)}));return[...e,...i]},[C,S,A]),{inference:nn,anonymous:Kn}=la(R),Yn=Gt(e=>e.user),Pt=R&&Kn.status==="expired",on=!Yn,Ye=xs();Re.current=Ye.disabled?Ye.disableReason||"This conversation is closed here.":void 0;const Ut=T===void 0?vs(n.sandbox,a):void 0,qe=st||Pt||!!qt||!!Ut||Ye.disabled,sn=Pt?on?a.demoKeyExpired:a.keyExpired:qt??Ut??wt??(Ye.disabled?Ye.disableReason:void 0),[$t,Je]=o.useState(""),[an]=Ss(),Qe=o.useMemo(()=>d?.voice?ys(d.voice,an):void 0,[d?.voice,an]),[qn,Jn]=o.useState(!1),[Qn,rn]=o.useState(),ft=Qn??Ge?.model??A?.model??n.model??"",Ue=Cs(),ln=o.useRef(Ue),cn=o.useRef(re),dn=o.useRef(Ue);dn.current=Ue,o.useEffect(()=>{cn.current!==re&&(cn.current=re,ln.current=dn.current,rn(void 0))},[re]);const Xn=Ue&&Ue!==ln.current?Ue:null,[Zn,eo]=o.useState(),to=Xn??Zn??null,no=Bt(Ce.inpromptMenu),un=Bt(Ce.promptAction),fn=Bt(Ce.chatHeader),oo=o.useMemo(()=>C?.members.map(e=>({id:e.id,name:e.name,description:e.description,icon:Le(e.icon)}))??[],[C]),be=Ln(e=>e.pending),[pn,so]=o.useState(0);o.useEffect(()=>ws(()=>so(e=>e+1)),[]),o.useEffect(()=>{be&&(Je(be.text),Ln.getState().consume(),be.submit&&he(be.text))},[be]);const mn=I.getConfig(vt)?.datalayerCreatePayload,xe=!!(Ge?.createPayload?.app_spec||mn?.app_spec),Xe=xa(mn),ao=Gt(e=>e.token),Ft=I.getConfig(vt)?.datalayerVisitors,io=ks(Ft),ro=I.getConfig(vt)?.embedToken,ve=Ft?io.token:ro||ao,lo=o.useMemo(()=>js(d?.voice,ve||void 0,Xe),[d?.voice,ve,Xe]),le=!!(Xe||Ft);if(le&&!xe)throw new Error("A deployment’s page runs its application: its host gave no app_spec.");const[gn]=o.useState(Pn),U=o.useMemo(()=>va(d?.thread,Xe,gn),[d?.thread,Xe,gn]),[$e,hn]=o.useState(null),co=!!(U?.fromRuntime&&$e?.given!==U.id);o.useEffect(()=>{if(!U?.fromRuntime||!xe||!T)return;let e=!0;return(async()=>{let i="";for(let s=0;s<4&&e;s+=1)try{const m=await Is({agentBaseUrl:T,agentId:g,uid:U.id,token:ve});e&&hn({given:U.id,thread:{...U,fromRuntime:!1,messages:m}});return}catch(m){i=m instanceof Error?m.message:String(m),await new Promise(u=>setTimeout(u,1500))}e&&hn({given:U.id,thread:{id:Pn(),messages:[],onStarted:U.onStarted},said:Un.notOpened(i)})})(),()=>{e=!1}},[U?.id,U?.fromRuntime,xe,T,g,ve]);const bn=U?.fromRuntime?$e?.given===U.id?$e.thread:void 0:U,Lt=o.useMemo(()=>R?aa({agentId:S?.specId??g,instructions:Es(A,Ge?.instructions),model:ft||void 0,inference:nn,frontendTools:Ke,subagents:C&&S?Mn(C.team,S.id):On(A),sharing:C?.sharing,onSubagentEvent:e=>Ks.getState().appendSubagentEvent(e),modeEffect:Ge?.modeEffect}):T!==void 0?{type:"ag-ui",endpoint:xe?`${T}/api/v1/apps/agents/${encodeURIComponent(g)}/ag-ui/`:`${T}/api/v1/ag-ui/${g}/`,...xe&&ve?{authToken:ve}:{},...xe?{uiSocket:`${T.replace(/^http/,"ws")}/api/v1/apps/ui`,uiFiles:`${T}/api/v1/apps/agents/${encodeURIComponent(g)}/files`,appSessions:`${T}/api/v1/apps/sessions`,...le?{ownRoutesOnly:!0}:{}}:{},agentId:g,configEndpoint:`${T}/api/v1/configure`,enableConfigQuery:!0}:void 0,[g,R,nn,S,Ke,C,A,Ge,ft,T,xe,ve,le]),pt=Ts(!R,Lt?.type==="ag-ui"?Lt.configEndpoint:void 0,void 0,g),Ze=Rs(),mt=As(g),[xn,Ot]=o.useState(void 0),[uo,fo]=o.useState(void 0);o.useEffect(()=>{if(R||T===void 0)return;const e=Ms(T,g,le);if(!e){Ot([]);return}let i=!1;return fetch(e).then(s=>s.ok?s.json():null).then(s=>{i||(Ot(s?Ps(s):[]),fo(s?Us(s):void 0))}).catch(()=>{i||Ot([])}),()=>{i=!0}},[T,g,R,le]);const po=o.useMemo(()=>$s(xn,()=>Object.values(Fs).filter(e=>e.available&&Ls(e)).map(e=>({id:e.id,name:e.name,provider:e.provider}))),[xn]),vn=o.useMemo(()=>{const e=pt.data?.builtinTools??[],i=new Set(e.map(s=>s.name));return{runtime:e,frontend:Ke.filter(s=>!i.has(s.name)).map(s=>({id:s.name,name:s.name}))}},[pt.data?.builtinTools,Ke]),Sn=o.useMemo(()=>new Set((Ze.data?.skills??[]).filter(e=>e.status==="enabled"||e.status==="loaded").map(e=>e.id)),[Ze.data]),mo=o.useCallback(async e=>{if(le)throw new Error("A deployment’s chat does not switch its runtime’s model.");rn(e),!(R||T===void 0)&&await fetch(`${T}/api/v1/configure/inference/provider`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({model:e})}).catch(()=>{})},[T,R,le]),yn=d?.promptPlacement==="bottom-chat",go=d?.promptPlacement==="floating",Fe=d?.promptPlacement==="top",et=d?.hidePrompt===!0||h.hidePrompt===!0,ho={onSend:()=>{he($t)},input:$t,setInput:Je,focusTrigger:be||pn?(be?.nonce??0)+pn:void 0,placement:go||J==="floating"||J==="floating-top"||J==="floating-bottom"?"floating":"docked",floatingAnchor:J==="floating-top"?"top":"bottom",isLoading:L,onStop:()=>n.viewControls.stop?.(),placeholder:sn??h.sendInstead?.says??we,typingSuggestions:Tt.map(e=>e.text),suggestions:dt,disableInputPrompt:qe,connectionConfirmed:!qe,interim:qn,padding:Fe||J==="floating-top"||J==="floating-bottom"||J==="docked-top"?2:3,promptVariant:"lexical",autoFocus:d?.autoFocusPrompt??!0,mentionableAgents:Gn,promptCommands:Dn,headerContent:no.length>0?t.jsx(St,{slot:Ce.inpromptMenu,props:{workspace:n}}):void 0,footerExtras:un.length>0||Qe&&Qe.input!=="off"?t.jsxs(t.Fragment,{children:[un.length>0?t.jsx(St,{slot:Ce.promptAction,props:{workspace:n}}):null,Qe&&Qe.input!=="off"?t.jsx(Os,{voice:Qe,input:$t,setInput:Je,disabled:qe,speaking:kt,onStopSpeaking:()=>r.current?.stopSpeaking?.(),onInterimChange:Jn,answers:!!(d?.voice?.output&&d.voice.output!=="off"&&d.voice.speechUrl),onSpoken:e=>{$.current=e},send:(e,i)=>{$.current=i,he(e)}}):null]}):void 0,showAgentsMenu:!_,showInlineAgentsMenu:!1,agents:oo,selectedAgentId:re,onSelectAgent:e=>C?.select(e),showTokenUsage:h.showTokenUsage??d?.showTokenUsage??!0,showContextRing:!0,agentUsage:to??void 0,showModelSelector:!_&&!le,models:po,decisions:uo,selectedModel:ft,onModelSelect:e=>{mo(e)},showToolsMenu:!_,availableTools:vn.runtime,availableFrontendTools:vn.frontend,mcpServers:pt.data?.mcpServers??[],mcpStatusData:h.mcpStatusData??void 0,showSkillsMenu:!_,skills:Ze.data?.skills??[],skillsLoading:Ze.isLoading,enabledSkills:Sn,onToggleSkill:e=>Sn.has(e)?mt.disableSkill(e):mt.enableSkill(e),onToggleAllSkills:(e,i)=>e.forEach(s=>i?mt.enableSkill(s):mt.disableSkill(s)),codemodeEnabled:h.codemodeEnabled??!1,onToggleCodemode:h.onToggleCodemode,isA2AProtocol:!1,hasConfigData:!!pt.data,hasSkillsData:!!Ze.data},Cn=te[0]?.value.Component,_t=Oe[0]?.value.Component,wn=!d?.hideHeader&&!!_t,gt=Cn?t.jsx(Cn,{workspace:n,composer:ho}):null,kn=E.length>0&&d?.showSurfaceSelector!==!1?t.jsx(Ta,{surfaces:E.map(e=>e.value),active:j?.surfaceId??P?.surfaceId??Z,onChange:Me,workspace:n}):null,jn=t.jsxs(t.Fragment,{children:[E.map(e=>{const i=e.value,s=j?.surfaceId===i.surfaceId;return s||X.current.has(i.surfaceId)||tt(i,n)?(X.current.add(i.surfaceId),t.jsx(p,{sx:s?{flex:"1 1 0",minWidth:0,minHeight:0}:{position:"absolute",inset:0,visibility:"hidden",pointerEvents:"none",zIndex:-1},children:t.jsx(_s,{load:i.load,props:{surfaceId:i.surfaceId,workspace:n},fallback:t.jsxs(Wt,{children:["Loading ",i.title,"…"]}),errorFallback:u=>t.jsxs(Wt,{children:[i.title," failed to load: ",u.message]})})},i.surfaceId)):null}),!j&&P?t.jsx(p,{flex:"1 1 0",minWidth:0,minHeight:0,children:t.jsxs(Wt,{children:["Starting ",P.title,"…"]})}):null]}),In=t.jsx(t.Fragment,{children:t.jsxs(p,{flex:Fe&&j?"0 0 34%":"1 1 0",maxWidth:Fe&&j?460:void 0,minWidth:0,minHeight:0,display:"flex",flexDirection:"column",borderLeft:j?"1px solid":void 0,borderColor:"border.default",position:"relative",children:[h.errorBanner?t.jsx(p,{flexShrink:0,px:3,py:2,fontSize:1,borderBottom:"1px solid",bg:h.errorBanner.variant==="warning"?"attention.subtle":"danger.subtle",color:h.errorBanner.variant==="warning"?"attention.fg":"danger.fg",borderColor:h.errorBanner.variant==="warning"?"attention.muted":"danger.muted",children:h.errorBanner.message}):null,$e?.said&&$e.given===U?.id?t.jsx(H,{as:"p",role:"status",sx:{m:0,px:3,py:2,fontSize:1,color:"fg.muted"},children:$e.said}):null,t.jsx(p,{flex:"1 1 auto",minHeight:0,display:"flex",children:co?t.jsx(H,{as:"p",role:"status",sx:{m:0,p:3,fontSize:1,color:"fg.muted"},children:Un.opening}):t.jsx(zs,{themeVariant:d?.themeVariant,themeOverrides:d?.themeOverrides,colorMode:d?.colorMode,thread:bn,voice:lo,disabled:qe,disableReason:sn,protocol:Lt,frontendTools:R?void 0:Wn,initialModel:ft||void 0,title:_?.name??S?.name??A?.name??g,description:_?.welcome??A?.description,brandIcon:_?.face?t.jsx(Ca,{face:_.face,size:20,state:fe}):t.jsx(Rt,{size:20}),headerContent:_&&!Ut?t.jsx(wa,{state:fe}):void 0,emptyState:{icon:_?.face?t.jsx("span",{"aria-hidden":!0,style:{fontSize:_n,lineHeight:1},children:t.jsx(Vn,{face:_.face,size:_n})}):t.jsx(Rt,{size:48}),title:_?.name??S?.name??A?.name??g,...C&&S?{sections:[{group:C.team.name,icon:t.jsx(Hn,{size:48}),title:C.team.name,subtitle:C.team.description},{group:S.name,icon:t.jsx(Rt,{size:32}),title:S.name,subtitle:A?.description}]}:null},suggestions:Fe||de||d?.suggestionLabels===!1?[]:dt,showHeader:wn,headerButtons:{showNewChat:d?.headerButtons?.newChat??!1,showClear:d?.headerButtons?.clear??!1},renderHeader:_t?e=>t.jsx(_t,{workspace:n,header:n.chatViewModes?{...e,chatViewMode:n.chatViewModes.value,onChatViewModeChange:n.chatViewModes.onChange,offeredViewModes:n.chatViewModes.offered}:e}):void 0,showInformation:!0,headerActions:t.jsxs(p,{display:"inline-flex",alignItems:"center",gap:2,children:[n.chatHeaderActions,fn.length>0?t.jsx(St,{slot:Ce.chatHeader,props:{workspace:n}}):null,t.jsx(Ns,{icon:G?Bs:Vs,"aria-label":G?"Exit full screen":"Enter full screen",variant:"invisible",size:"small",onClick:()=>{c(!1),Ve()},sx:We?{"@keyframes dla-fullscreen-hint":{"0%, 100%":{opacity:1},"50%":{opacity:.4}},"&&":{bg:`color-mix(in srgb, ${l.primary} 16%, transparent)`,boxShadow:`inset 0 0 0 1.5px ${l.primary}`,animation:`dla-fullscreen-hint ${Ea}ms ease-in-out infinite`},"@media (prefers-reduced-motion: reduce)":{"&&":{animation:"none"}}}:void 0})]}),showInput:!1,onSendReady:it,notebookToolSurfacesId:j?void 0:n.surfaceId,showTurnFooter:h.showTokenUsage??d?.showTokenUsage??!0,onContextSnapshot:O,onLoadingChange:It,onSpeakingChange:at,onItemsChange:me,onDisplayItemsChange:Ne,enableStreaming:!0},bn?.id)}),!wn&&fn.length>0?t.jsx(p,{hidden:!0,children:t.jsx(St,{slot:Ce.chatHeader,props:{workspace:n}})}):null,yn&&!et?gt:null]})}),En=d?.suggestionLabels!==!1&&dt.length>0?t.jsx(p,{"aria-label":a.suggestedPrompts,display:"flex",flexWrap:"wrap",gap:2,px:2,pb:"6px",children:dt.map(e=>t.jsx(p,{as:"button",type:"button",title:e.message,disabled:L||qe,onClick:()=>{he(e.message)},appearance:"none",font:"inherit",fontSize:0,fontWeight:"semibold",lineHeight:1.25,px:"10px",py:"5px",width:$n,flex:`0 0 ${$n}px`,borderRadius:"999px",border:"1px solid",bg:"canvas.subtle",whiteSpace:"nowrap",sx:{borderColor:"border.default",color:"fg.default",cursor:"pointer","&:hover:not(:disabled)":{borderColor:"accent.fg",color:"accent.fg"},"&:disabled":{opacity:.5,cursor:"default"}},children:t.jsx(Hs,{title:e.title,maxWidth:"100%",sx:{minWidth:0},children:e.title})},e.title))}):null,Tn=x?t.jsx(p,{flex:"0 0 auto",px:3,py:2,borderTop:"1px solid",borderColor:"border.default",bg:"canvas.subtle",fontSize:1,maxHeight:"40%",overflowY:"auto",children:x}):null;return t.jsxs(p,{ref:lt,sx:{height:"100%",minHeight:0,display:"flex",flexDirection:"column",position:"relative",...G?{bg:"canvas.default"}:null,...G&&!Be.current?{position:"fixed",top:He,right:0,bottom:0,left:0,height:"auto",zIndex:1e3}:null},children:[de?t.jsx(de.Component,{workspace:n,editors:jn,hasEditor:!!j,transcript:In,prompt:et?null:gt,chips:et?null:En,picker:kn,transient:Tn}):t.jsxs(t.Fragment,{children:[Fe&&!et?t.jsxs(p,{flex:"0 0 auto",borderBottom:"1px solid",borderColor:"border.default",children:[gt,En]}):null,kn,t.jsxs(p,{flex:"1 1 auto",minHeight:0,display:"flex",position:"relative",children:[jn,In]}),Tn,yn||et||Fe?null:gt]}),Pt?t.jsx(p,{position:"absolute",inset:0,zIndex:5,bg:"canvas.default",display:"flex",flexDirection:"column",minHeight:0,children:t.jsx(ia,{agentName:S?.name??A?.name,sandboxStillRuns:R,temporary:on,onSignedIn:()=>Ws.getState().clear()})}):null]})}function Ta({surfaces:n,active:a,onChange:l,workspace:r}){const f=[...n].sort((x,y)=>(x.order??100)-(y.order??100));return t.jsxs(p,{flex:"0 0 auto",display:"flex",alignItems:"center",gap:2,px:3,py:2,borderBottom:"1px solid",borderColor:"border.default",children:[t.jsx(H,{sx:{fontSize:0,color:"fg.muted"},children:"Beside the chat"}),t.jsxs(Vt,{"aria-label":"Editor beside the chat",size:"small",children:[t.jsx(Vt.Button,{selected:a===Z,onClick:()=>l(Z),children:"Chat"}),f.map(x=>{const y=tt(x,r),I=y?void 0:x.unavailableReason?.(r)??"Not available right now";return t.jsx(Vt.Button,{selected:a===x.surfaceId,"aria-disabled":!y,title:I??x.title,leadingIcon:x.icon,onClick:()=>{y&&l(x.surfaceId)},children:x.title},x.surfaceId)})]})]})}function Wt({children:n}){return t.jsx(p,{height:"100%",display:"flex",alignItems:"center",justifyContent:"center",color:"fg.muted",fontSize:1,px:3,textAlign:"center",children:n})}export{Pa as default};
|