@jupyter-ai/persona-manager 0.2.0-a3 → 0.2.0-a4

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/lib/index.js CHANGED
@@ -76,9 +76,17 @@ const toolbarPlugin = {
76
76
  provides: IInputToolbarRegistryFactory,
77
77
  requires: [IPersonaControlRegistry, IPersonaSessionRegistry],
78
78
  activate: (app, controlRegistry, sessionRegistry) => {
79
- // Wrap the persona controls to inject the control + session registries,
80
- // which the generic toolbar-item props don't carry.
81
- const PersonaControlsItem = (itemProps) => PersonaControls({ ...itemProps, controlRegistry, sessionRegistry });
79
+ // The event bus front-end; `PersonaControls` uses it to emit
80
+ // `persona_selected` on selection.
81
+ const events = app.serviceManager.events;
82
+ // Wrap the persona controls to inject the control + session registries and
83
+ // the event manager, which the generic toolbar-item props don't carry.
84
+ const PersonaControlsItem = (itemProps) => PersonaControls({
85
+ ...itemProps,
86
+ controlRegistry,
87
+ sessionRegistry,
88
+ events
89
+ });
82
90
  return {
83
91
  create: () => {
84
92
  // Start with the default toolbar (Send, Attach, Cancel, SaveEdit)
@@ -1,3 +1,4 @@
1
+ import { Event } from '@jupyterlab/services';
1
2
  import { InputToolbarRegistry } from '@jupyter/chat';
2
3
  import { PersonaOption, Usage } from './awareness';
3
4
  import { PersonaSessionRegistry, PersonaSessionState } from './persona-events';
@@ -177,5 +178,10 @@ export declare function PersonaControls(props: InputToolbarRegistry.IToolbarItem
177
178
  * Optional so the component still works without it (renders nothing).
178
179
  */
179
180
  sessionRegistry?: PersonaSessionRegistry;
181
+ /**
182
+ * The event bus front-end, used to emit a `persona_selected` event when the
183
+ * user picks a persona so the server prepares it eagerly.
184
+ */
185
+ events?: Event.IManager;
180
186
  }): JSX.Element | null;
181
187
  export {};
@@ -6,6 +6,7 @@ import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
6
6
  import { PageConfig } from '@jupyterlab/coreutils';
7
7
  import { EMPTY_USAGE } from './awareness';
8
8
  import { buildMessageMetadata, emptyPersonaSettings } from './metadata';
9
+ import { PERSONA_SELECTED_EVENT_SCHEMA_ID } from './persona-events';
9
10
  const SELECTOR_CLASS = 'jp-jai-personaControls';
10
11
  const MENU_CLASS = 'jp-jai-controlMenu';
11
12
  const USAGE_CLASS = 'jp-jai-usage';
@@ -635,7 +636,7 @@ export function UsageChip(props) {
635
636
  */
636
637
  export function PersonaControls(props) {
637
638
  var _a, _b, _c, _d, _e, _f;
638
- const { chatModel, model, controlRegistry, sessionRegistry } = props;
639
+ const { chatModel, model, controlRegistry, sessionRegistry, events } = props;
639
640
  // The chat's stable id scopes persona events to this chat. It is assigned
640
641
  // asynchronously (once the model is `ready`: the WS connection frame arrives,
641
642
  // or the RTC document syncs), so track it in state and update it when the
@@ -748,6 +749,21 @@ export function PersonaControls(props) {
748
749
  model.clearMetadata();
749
750
  model.updateMetadata(buildMessageMetadata(selectedId, settings));
750
751
  }, [model, metadataSignature]);
752
+ // On selection, tell the server to prepare the persona by
753
+ // emitting `persona_selected` over the event bus, so its controls appear
754
+ // before the first message. Skips "No one"; idempotent server-side.
755
+ useEffect(() => {
756
+ if (!events || !chatId || !selectedId) {
757
+ return;
758
+ }
759
+ void events
760
+ .emit({
761
+ schema_id: PERSONA_SELECTED_EVENT_SCHEMA_ID,
762
+ version: '1',
763
+ data: { chat_id: chatId, persona_id: selectedId }
764
+ })
765
+ .catch(e => console.warn('Error emitting persona_selected event: ', e));
766
+ }, [events, chatId, selectedId]);
751
767
  // No personas yet. While the manager's slot or its first list read is still
752
768
  // pending, show a loading placeholder (on slow networks this takes seconds);
753
769
  // once ready with no personas, show nothing.
@@ -5,6 +5,7 @@ import { ISignal } from '@lumino/signaling';
5
5
  import { CommandOption, ModelConfiguration, PersonaOption, SettingConfiguration, Usage } from './awareness';
6
6
  export declare const PERSONAS_EVENT_SCHEMA_ID = "https://schema.jupyter.org/jupyter_ai_persona_manager/personas/v1";
7
7
  export declare const PERSONA_STATE_EVENT_SCHEMA_ID = "https://schema.jupyter.org/jupyter_ai_persona_manager/persona_state/v1";
8
+ export declare const PERSONA_SELECTED_EVENT_SCHEMA_ID = "https://schema.jupyter.org/jupyter_ai_persona_manager/persona_selected/v1";
8
9
  /** The wire shape of a `persona_state` event. */
9
10
  type PersonaStatePayload = {
10
11
  chat_id?: string;
@@ -3,6 +3,7 @@ import { Signal } from '@lumino/signaling';
3
3
  import { EMPTY_MODEL_CONFIGURATION, EMPTY_USAGE } from './awareness';
4
4
  export const PERSONAS_EVENT_SCHEMA_ID = 'https://schema.jupyter.org/jupyter_ai_persona_manager/personas/v1';
5
5
  export const PERSONA_STATE_EVENT_SCHEMA_ID = 'https://schema.jupyter.org/jupyter_ai_persona_manager/persona_state/v1';
6
+ export const PERSONA_SELECTED_EVENT_SCHEMA_ID = 'https://schema.jupyter.org/jupyter_ai_persona_manager/persona_selected/v1';
6
7
  /**
7
8
  * One persona's live session state, built from a `persona_state` event.
8
9
  * Immutable: each update produces a new instance, so React consumers see a new
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupyter-ai/persona-manager",
3
- "version": "0.2.0-a3",
3
+ "version": "0.2.0-a4",
4
4
  "description": "The core manager & registry for AI personas in Jupyter AI",
5
5
  "keywords": [
6
6
  "jupyter",
package/src/index.ts CHANGED
@@ -119,11 +119,20 @@ const toolbarPlugin: JupyterFrontEndPlugin<IInputToolbarRegistryFactory> = {
119
119
  controlRegistry: IPersonaControlRegistry,
120
120
  sessionRegistry: PersonaSessionRegistry
121
121
  ): IInputToolbarRegistryFactory => {
122
- // Wrap the persona controls to inject the control + session registries,
123
- // which the generic toolbar-item props don't carry.
122
+ // The event bus front-end; `PersonaControls` uses it to emit
123
+ // `persona_selected` on selection.
124
+ const events = app.serviceManager.events;
125
+ // Wrap the persona controls to inject the control + session registries and
126
+ // the event manager, which the generic toolbar-item props don't carry.
124
127
  const PersonaControlsItem = (
125
128
  itemProps: InputToolbarRegistry.IToolbarItemProps
126
- ) => PersonaControls({ ...itemProps, controlRegistry, sessionRegistry });
129
+ ) =>
130
+ PersonaControls({
131
+ ...itemProps,
132
+ controlRegistry,
133
+ sessionRegistry,
134
+ events
135
+ });
127
136
  return {
128
137
  create: () => {
129
138
  // Start with the default toolbar (Send, Attach, Cancel, SaveEdit)
@@ -21,6 +21,7 @@ import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
21
21
  import CheckIcon from '@mui/icons-material/Check';
22
22
  import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
23
23
  import { PageConfig } from '@jupyterlab/coreutils';
24
+ import { Event } from '@jupyterlab/services';
24
25
  import { InputToolbarRegistry } from '@jupyter/chat';
25
26
  import {
26
27
  EMPTY_USAGE,
@@ -35,6 +36,7 @@ import {
35
36
  emptyPersonaSettings
36
37
  } from './metadata';
37
38
  import { IPersonaControlRegistry } from './persona-control-registry';
39
+ import { PERSONA_SELECTED_EVENT_SCHEMA_ID } from './persona-events';
38
40
 
39
41
  const SELECTOR_CLASS = 'jp-jai-personaControls';
40
42
  const MENU_CLASS = 'jp-jai-controlMenu';
@@ -1072,9 +1074,14 @@ export function PersonaControls(
1072
1074
  * Optional so the component still works without it (renders nothing).
1073
1075
  */
1074
1076
  sessionRegistry?: PersonaSessionRegistry;
1077
+ /**
1078
+ * The event bus front-end, used to emit a `persona_selected` event when the
1079
+ * user picks a persona so the server prepares it eagerly.
1080
+ */
1081
+ events?: Event.IManager;
1075
1082
  }
1076
1083
  ): JSX.Element | null {
1077
- const { chatModel, model, controlRegistry, sessionRegistry } = props;
1084
+ const { chatModel, model, controlRegistry, sessionRegistry, events } = props;
1078
1085
  // The chat's stable id scopes persona events to this chat. It is assigned
1079
1086
  // asynchronously (once the model is `ready`: the WS connection frame arrives,
1080
1087
  // or the RTC document syncs), so track it in state and update it when the
@@ -1207,6 +1214,22 @@ export function PersonaControls(
1207
1214
  model.updateMetadata(buildMessageMetadata(selectedId, settings));
1208
1215
  }, [model, metadataSignature]);
1209
1216
 
1217
+ // On selection, tell the server to prepare the persona by
1218
+ // emitting `persona_selected` over the event bus, so its controls appear
1219
+ // before the first message. Skips "No one"; idempotent server-side.
1220
+ useEffect(() => {
1221
+ if (!events || !chatId || !selectedId) {
1222
+ return;
1223
+ }
1224
+ void events
1225
+ .emit({
1226
+ schema_id: PERSONA_SELECTED_EVENT_SCHEMA_ID,
1227
+ version: '1',
1228
+ data: { chat_id: chatId, persona_id: selectedId }
1229
+ })
1230
+ .catch(e => console.warn('Error emitting persona_selected event: ', e));
1231
+ }, [events, chatId, selectedId]);
1232
+
1210
1233
  // No personas yet. While the manager's slot or its first list read is still
1211
1234
  // pending, show a loading placeholder (on slow networks this takes seconds);
1212
1235
  // once ready with no personas, show nothing.
@@ -31,6 +31,8 @@ export const PERSONAS_EVENT_SCHEMA_ID =
31
31
  'https://schema.jupyter.org/jupyter_ai_persona_manager/personas/v1';
32
32
  export const PERSONA_STATE_EVENT_SCHEMA_ID =
33
33
  'https://schema.jupyter.org/jupyter_ai_persona_manager/persona_state/v1';
34
+ export const PERSONA_SELECTED_EVENT_SCHEMA_ID =
35
+ 'https://schema.jupyter.org/jupyter_ai_persona_manager/persona_selected/v1';
34
36
 
35
37
  /** The wire shape of a `persona_state` event. */
36
38
  type PersonaStatePayload = {