appkit-assistant 0.16.3__py3-none-any.whl → 0.17.1__py3-none-any.whl

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.
@@ -12,6 +12,7 @@ from appkit_assistant.state.thread_state import (
12
12
  ThreadState,
13
13
  )
14
14
  from appkit_ui.components.collabsible import collabsible
15
+ from appkit_ui.components.dialogs import delete_dialog
15
16
 
16
17
  message_styles = {
17
18
  "spacing": "4",
@@ -85,29 +86,196 @@ class AuthCardComponent:
85
86
  )
86
87
 
87
88
 
88
- class MessageComponent:
89
+ class MessageActionsBar:
90
+ """Component for message action buttons (copy, download, retry)."""
91
+
89
92
  @staticmethod
90
- def human_message(message: str) -> rx.Component:
93
+ def render(message: Message) -> rx.Component:
91
94
  return rx.hstack(
92
- rx.spacer(),
93
- rx.box(
94
- rx.text(
95
- message,
96
- padding="0.5em",
97
- border_radius="10px",
98
- white_space="pre-line",
95
+ rx.tooltip(
96
+ rx.icon_button(
97
+ rx.icon("copy", size=14),
98
+ on_click=ThreadState.copy_message(message.text),
99
+ variant="ghost",
100
+ size="1",
101
+ color_scheme="gray",
99
102
  ),
100
- padding="4px",
101
- max_width="80%",
102
- margin_top="24px",
103
- # margin_right="14px",
104
- background_color=rx.color_mode_cond(
105
- light=rx.color("accent", 3),
106
- dark=rx.color("accent", 3),
103
+ content="Kopieren",
104
+ ),
105
+ rx.tooltip(
106
+ rx.icon_button(
107
+ rx.icon("download", size=14),
108
+ on_click=ThreadState.download_message(message.text, message.id),
109
+ variant="ghost",
110
+ size="1",
111
+ color_scheme="gray",
107
112
  ),
108
- border_radius="9px",
113
+ content="Herunterladen",
114
+ ),
115
+ rx.tooltip(
116
+ delete_dialog(
117
+ title="Nachricht löschen",
118
+ content="diese Nachricht",
119
+ on_click=ThreadState.delete_message(message.id),
120
+ icon_button=True,
121
+ variant="ghost",
122
+ size="1",
123
+ color_scheme="gray",
124
+ ),
125
+ content="Löschen",
126
+ ),
127
+ rx.cond(
128
+ (message.type == MessageType.ASSISTANT)
129
+ | (message.type == MessageType.ERROR),
130
+ rx.tooltip(
131
+ rx.icon_button(
132
+ rx.cond(
133
+ ThreadState.processing,
134
+ rx.spinner(size="1"),
135
+ rx.icon("refresh-cw", size=14),
136
+ ),
137
+ on_click=ThreadState.retry_message(message.id),
138
+ variant="ghost",
139
+ size="1",
140
+ color_scheme="gray",
141
+ disabled=ThreadState.processing,
142
+ ),
143
+ content="Erneut generieren (folgende Nachrichten werden entfernt)",
144
+ ),
145
+ rx.fragment(),
146
+ ),
147
+ spacing="3",
148
+ margin_top="-9px",
149
+ margin_left="9px",
150
+ )
151
+
152
+
153
+ class MessageComponent:
154
+ @staticmethod
155
+ def _file_badge(filename: str) -> rx.Component:
156
+ """Render a single file attachment badge."""
157
+ return rx.badge(
158
+ rx.icon("paperclip", size=12),
159
+ filename,
160
+ variant="soft",
161
+ color="gray",
162
+ size="1",
163
+ radius="small",
164
+ )
165
+
166
+ @staticmethod
167
+ def _attachments_row(attachments: list[str]) -> rx.Component:
168
+ """Render a row of file attachment badges."""
169
+ return rx.cond(
170
+ attachments.length() > 0,
171
+ rx.hstack(
172
+ rx.foreach(attachments, MessageComponent._file_badge),
173
+ spacing="2",
174
+ margin_top="8px",
175
+ justify="end",
176
+ width="90%",
177
+ flex_wrap="wrap",
178
+ ),
179
+ rx.fragment(),
180
+ )
181
+
182
+ @staticmethod
183
+ def human_message(message: Message) -> rx.Component:
184
+ return rx.cond(
185
+ ThreadState.editing_message_id == message.id,
186
+ # Edit Mode
187
+ rx.vstack(
188
+ rx.text_area(
189
+ value=ThreadState.edited_message_content,
190
+ on_change=ThreadState.set_edited_message_content,
191
+ height="112px",
192
+ width="824px",
193
+ auto_focus=True,
194
+ bg=rx.color("gray", 3),
195
+ variant="soft",
196
+ ),
197
+ rx.hstack(
198
+ rx.button(
199
+ "Abbrechen",
200
+ on_click=ThreadState.cancel_edit,
201
+ variant="soft",
202
+ color_scheme="gray",
203
+ ),
204
+ rx.button("Senden", on_click=ThreadState.submit_edited_message),
205
+ justify="end",
206
+ width="100%",
207
+ spacing="2",
208
+ ),
209
+ style=message_styles,
210
+ align="end",
211
+ ),
212
+ rx.vstack(
213
+ rx.hstack(
214
+ rx.spacer(),
215
+ rx.vstack(
216
+ rx.box(
217
+ rx.text(
218
+ message.text,
219
+ padding="0.5em",
220
+ border_radius="10px",
221
+ white_space="pre-line",
222
+ ),
223
+ padding="4px",
224
+ max_width="800px",
225
+ background_color=rx.color_mode_cond(
226
+ light=rx.color("accent", 3),
227
+ dark=rx.color("accent", 3),
228
+ ),
229
+ border_radius="9px",
230
+ ),
231
+ MessageComponent._attachments_row(message.attachments),
232
+ align="end",
233
+ spacing="1",
234
+ ),
235
+ ),
236
+ rx.hstack(
237
+ rx.spacer(),
238
+ rx.tooltip(
239
+ rx.icon_button(
240
+ rx.icon("pencil", size=14),
241
+ on_click=ThreadState.set_editing_mode(
242
+ message.id, message.text
243
+ ),
244
+ variant="ghost",
245
+ size="1",
246
+ color_scheme="gray",
247
+ ),
248
+ content="Bearbeiten",
249
+ ),
250
+ rx.tooltip(
251
+ delete_dialog(
252
+ title="Nachricht löschen",
253
+ content="diese Nachricht",
254
+ on_click=ThreadState.delete_message(message.id),
255
+ icon_button=True,
256
+ variant="ghost",
257
+ size="1",
258
+ color_scheme="gray",
259
+ ),
260
+ content="Löschen",
261
+ ),
262
+ rx.tooltip(
263
+ rx.icon_button(
264
+ rx.icon("copy", size=14),
265
+ on_click=ThreadState.copy_message(message.text),
266
+ variant="ghost",
267
+ size="1",
268
+ color_scheme="gray",
269
+ ),
270
+ content="Kopieren",
271
+ ),
272
+ spacing="3",
273
+ justify="end",
274
+ margin_right="9px",
275
+ ),
276
+ align="end",
277
+ style=message_styles,
109
278
  ),
110
- style=message_styles,
111
279
  )
112
280
 
113
281
  @staticmethod
@@ -157,12 +325,6 @@ class MessageComponent:
157
325
  color=rx.color("gray", 8),
158
326
  margin_right="9px",
159
327
  ),
160
- rx.hstack(
161
- rx.el.span(""),
162
- rx.el.span(""),
163
- rx.el.span(""),
164
- rx.el.span(""),
165
- ),
166
328
  class_name="loading",
167
329
  height="40px",
168
330
  color=rx.color("gray", 8),
@@ -173,23 +335,24 @@ class MessageComponent:
173
335
  padding_right="18px",
174
336
  ),
175
337
  # Actual message content
176
- mn.markdown_preview(
177
- source=message.text,
178
- enable_mermaid=message.done,
179
- enable_katex=message.done,
180
- security_level="standard",
338
+ rx.box(
339
+ mn.markdown_preview(
340
+ source=message.text,
341
+ enable_mermaid=message.done,
342
+ enable_katex=message.done,
343
+ security_level="standard",
344
+ class_name="markdown",
345
+ ),
181
346
  padding="0.5em",
182
- border_radius="9px",
347
+ margin_top="18px",
183
348
  max_width="90%",
184
- class_name="markdown",
185
349
  ),
186
- # rx.markdown(
187
- # message.text,
188
- # padding="0.5em",
189
- # border_radius="9px",
190
- # max_width="90%",
191
- # class_name="markdown",
192
- # ),
350
+ ),
351
+ # Actions bar
352
+ rx.cond(
353
+ message.done,
354
+ MessageActionsBar.render(message),
355
+ rx.fragment(),
193
356
  ),
194
357
  spacing="3",
195
358
  width="100%",
@@ -230,7 +393,7 @@ class MessageComponent:
230
393
  )
231
394
 
232
395
  @staticmethod
233
- def error_message(message: str) -> rx.Component:
396
+ def error_message(message: Message) -> rx.Component:
234
397
  return rx.hstack(
235
398
  rx.avatar(
236
399
  fallback="!",
@@ -240,15 +403,20 @@ class MessageComponent:
240
403
  margin_top="16px",
241
404
  color_scheme="red",
242
405
  ),
243
- rx.callout(
244
- message,
245
- icon="triangle-alert",
246
- color_scheme="red",
247
- max_width="90%",
248
- size="1",
249
- padding="0.5em",
250
- border_radius="9px",
251
- margin_top="18px",
406
+ rx.vstack(
407
+ rx.callout(
408
+ message.text,
409
+ icon="triangle-alert",
410
+ color_scheme="red",
411
+ max_width="100%",
412
+ size="1",
413
+ padding="0.5em",
414
+ border_radius="9px",
415
+ margin_top="18px",
416
+ ),
417
+ MessageActionsBar.render(message),
418
+ width="90%",
419
+ spacing="2",
252
420
  ),
253
421
  style=message_styles,
254
422
  )
@@ -287,7 +455,7 @@ class MessageComponent:
287
455
  message.type,
288
456
  (
289
457
  MessageType.HUMAN,
290
- MessageComponent.human_message(message.text),
458
+ MessageComponent.human_message(message),
291
459
  ),
292
460
  (
293
461
  MessageType.ASSISTANT,
@@ -295,7 +463,7 @@ class MessageComponent:
295
463
  ),
296
464
  (
297
465
  MessageType.ERROR,
298
- MessageComponent.error_message(message.text),
466
+ MessageComponent.error_message(message),
299
467
  ),
300
468
  (
301
469
  MessageType.SYSTEM,
@@ -113,16 +113,17 @@ class Assistant:
113
113
  **props,
114
114
  ) -> rx.Component:
115
115
  return composer(
116
+ composer.selected_files_row(),
116
117
  composer.input(),
117
118
  rx.hstack(
118
119
  rx.hstack(
119
120
  composer.choose_model(show=with_model_chooser),
120
121
  ),
121
122
  rx.hstack(
123
+ composer.file_upload(show=with_attachments),
122
124
  composer.tools(
123
125
  show=with_tools and ThreadState.selected_model_supports_tools
124
126
  ),
125
- composer.add_attachment(show=with_attachments),
126
127
  composer.clear(show=with_clear),
127
128
  composer.submit(),
128
129
  width="100%",
@@ -7,5 +7,7 @@ class AssistantConfig(BaseConfig):
7
7
  perplexity_api_key: SecretStr | None = None
8
8
  openai_base_url: str | None = None
9
9
  openai_api_key: SecretStr | None = None
10
+ claude_base_url: str | None = None
11
+ claude_api_key: SecretStr | None = None
10
12
  google_api_key: SecretStr | None = None
11
13
  azure_ai_projects_endpoint: str | None = None