appkit-assistant 0.17.3__py3-none-any.whl → 1.0.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.
Files changed (57) hide show
  1. appkit_assistant/backend/{models.py → database/models.py} +32 -132
  2. appkit_assistant/backend/{repositories.py → database/repositories.py} +93 -1
  3. appkit_assistant/backend/model_manager.py +5 -5
  4. appkit_assistant/backend/models/__init__.py +28 -0
  5. appkit_assistant/backend/models/anthropic.py +31 -0
  6. appkit_assistant/backend/models/google.py +27 -0
  7. appkit_assistant/backend/models/openai.py +50 -0
  8. appkit_assistant/backend/models/perplexity.py +56 -0
  9. appkit_assistant/backend/processors/__init__.py +29 -0
  10. appkit_assistant/backend/processors/claude_responses_processor.py +205 -387
  11. appkit_assistant/backend/processors/gemini_responses_processor.py +290 -352
  12. appkit_assistant/backend/processors/lorem_ipsum_processor.py +6 -4
  13. appkit_assistant/backend/processors/mcp_mixin.py +297 -0
  14. appkit_assistant/backend/processors/openai_base.py +11 -125
  15. appkit_assistant/backend/processors/openai_chat_completion_processor.py +5 -3
  16. appkit_assistant/backend/processors/openai_responses_processor.py +480 -402
  17. appkit_assistant/backend/processors/perplexity_processor.py +156 -79
  18. appkit_assistant/backend/{processor.py → processors/processor_base.py} +7 -2
  19. appkit_assistant/backend/processors/streaming_base.py +188 -0
  20. appkit_assistant/backend/schemas.py +138 -0
  21. appkit_assistant/backend/services/auth_error_detector.py +99 -0
  22. appkit_assistant/backend/services/chunk_factory.py +273 -0
  23. appkit_assistant/backend/services/citation_handler.py +292 -0
  24. appkit_assistant/backend/services/file_cleanup_service.py +316 -0
  25. appkit_assistant/backend/services/file_upload_service.py +903 -0
  26. appkit_assistant/backend/services/file_validation.py +138 -0
  27. appkit_assistant/backend/{mcp_auth_service.py → services/mcp_auth_service.py} +4 -2
  28. appkit_assistant/backend/services/mcp_token_service.py +61 -0
  29. appkit_assistant/backend/services/message_converter.py +289 -0
  30. appkit_assistant/backend/services/openai_client_service.py +120 -0
  31. appkit_assistant/backend/{response_accumulator.py → services/response_accumulator.py} +163 -1
  32. appkit_assistant/backend/services/system_prompt_builder.py +89 -0
  33. appkit_assistant/backend/services/thread_service.py +5 -3
  34. appkit_assistant/backend/system_prompt_cache.py +3 -3
  35. appkit_assistant/components/__init__.py +8 -4
  36. appkit_assistant/components/composer.py +59 -24
  37. appkit_assistant/components/file_manager.py +623 -0
  38. appkit_assistant/components/mcp_server_dialogs.py +12 -20
  39. appkit_assistant/components/mcp_server_table.py +12 -2
  40. appkit_assistant/components/message.py +119 -2
  41. appkit_assistant/components/thread.py +1 -1
  42. appkit_assistant/components/threadlist.py +4 -2
  43. appkit_assistant/components/tools_modal.py +37 -20
  44. appkit_assistant/configuration.py +12 -0
  45. appkit_assistant/state/file_manager_state.py +697 -0
  46. appkit_assistant/state/mcp_oauth_state.py +3 -3
  47. appkit_assistant/state/mcp_server_state.py +47 -2
  48. appkit_assistant/state/system_prompt_state.py +1 -1
  49. appkit_assistant/state/thread_list_state.py +99 -5
  50. appkit_assistant/state/thread_state.py +88 -9
  51. {appkit_assistant-0.17.3.dist-info → appkit_assistant-1.0.1.dist-info}/METADATA +8 -6
  52. appkit_assistant-1.0.1.dist-info/RECORD +58 -0
  53. appkit_assistant/backend/processors/claude_base.py +0 -178
  54. appkit_assistant/backend/processors/gemini_base.py +0 -84
  55. appkit_assistant-0.17.3.dist-info/RECORD +0 -39
  56. /appkit_assistant/backend/{file_manager.py → services/file_manager.py} +0 -0
  57. {appkit_assistant-0.17.3.dist-info → appkit_assistant-1.0.1.dist-info}/WHEEL +0 -0
@@ -9,8 +9,8 @@ from reflex.vars import var_operation, var_operation_return
9
9
  from reflex.vars.base import RETURN, CustomVarOperationReturn
10
10
 
11
11
  import appkit_mantine as mn
12
- from appkit_assistant.backend.mcp_auth_service import MCPAuthService
13
- from appkit_assistant.backend.models import MCPAuthType, MCPServer
12
+ from appkit_assistant.backend.database.models import MCPAuthType, MCPServer
13
+ from appkit_assistant.backend.services.mcp_auth_service import MCPAuthService
14
14
  from appkit_assistant.state.mcp_server_state import MCPServerState
15
15
  from appkit_ui.components.dialogs import (
16
16
  delete_dialog,
@@ -280,7 +280,7 @@ def json(obj: rx.Var, indent: int = 4) -> CustomVarOperationReturn[RETURN]:
280
280
  def _auth_type_selector() -> rx.Component:
281
281
  """Radio for selecting authentication type."""
282
282
  return rx.box(
283
- rx.text("Authentifizierung", size="2", weight="medium", mb="2"),
283
+ rx.heading("Authentifizierung", size="3", margin_bottom="9px"),
284
284
  rx.radio_group.root(
285
285
  rx.flex(
286
286
  rx.flex(
@@ -302,7 +302,7 @@ def _auth_type_selector() -> rx.Component:
302
302
  name="auth_type",
303
303
  ),
304
304
  width="100%",
305
- mb="3",
305
+ margin_bottom="12px",
306
306
  )
307
307
 
308
308
 
@@ -338,18 +338,6 @@ def _oauth_auth_fields(server: MCPServer | None = None) -> rx.Component:
338
338
  return rx.cond(
339
339
  ValidationState.is_oauth_mode,
340
340
  rx.flex(
341
- rx.box(
342
- rx.callout(
343
- "OAuth 2.0 ermöglicht eine sichere Anmeldung über den "
344
- "Identitätsanbieter des MCP-Servers. Die OAuth-Endpunkte "
345
- "können automatisch ermittelt oder manuell konfiguriert werden.",
346
- icon="info",
347
- size="1",
348
- color="blue",
349
- ),
350
- width="100%",
351
- mb="3",
352
- ),
353
341
  # Primary Fields (Client ID / Secret)
354
342
  form_field(
355
343
  name="oauth_client_id",
@@ -364,7 +352,7 @@ def _oauth_auth_fields(server: MCPServer | None = None) -> rx.Component:
364
352
  on_change=ValidationState.set_oauth_client_id,
365
353
  on_blur=ValidationState.validate_oauth_client_id,
366
354
  validation_error=ValidationState.oauth_client_id_error,
367
- autocomplete="off",
355
+ autocomplete="one-time-code",
368
356
  ),
369
357
  form_field(
370
358
  name="oauth_client_secret",
@@ -379,9 +367,9 @@ def _oauth_auth_fields(server: MCPServer | None = None) -> rx.Component:
379
367
  on_change=ValidationState.set_oauth_client_secret,
380
368
  on_blur=ValidationState.validate_oauth_client_secret,
381
369
  validation_error=ValidationState.oauth_client_secret_error,
382
- autocomplete="off",
370
+ autocomplete="new-password",
383
371
  ),
384
- rx.text("OAuth Endpunkte & Scopes", size="2", weight="medium", mb="1"),
372
+ rx.heading("OAuth Endpunkte & Scopes", size="3", margin_top="12px"),
385
373
  # Additional Discovery Fields (Editable)
386
374
  form_field(
387
375
  name="oauth_issuer",
@@ -579,7 +567,11 @@ def add_mcp_server_button() -> rx.Component:
579
567
  ),
580
568
  rx.flex(
581
569
  rx.form.root(
582
- mcp_server_form_fields(),
570
+ mn.scroll_area(
571
+ mcp_server_form_fields(),
572
+ height="60vh",
573
+ width="100%",
574
+ ),
583
575
  dialog_buttons(
584
576
  "MCP Server anlegen",
585
577
  has_errors=ValidationState.has_errors,
@@ -3,7 +3,7 @@
3
3
  import reflex as rx
4
4
  from reflex.components.radix.themes.components.table import TableRow
5
5
 
6
- from appkit_assistant.backend.models import MCPServer
6
+ from appkit_assistant.backend.database.models import MCPServer
7
7
  from appkit_assistant.components.mcp_server_dialogs import (
8
8
  add_mcp_server_button,
9
9
  delete_mcp_server_dialog,
@@ -36,6 +36,15 @@ def mcp_server_table_row(server: MCPServer) -> TableRow:
36
36
  "width": "100%",
37
37
  },
38
38
  ),
39
+ rx.table.cell(
40
+ rx.switch(
41
+ checked=server.active,
42
+ on_change=lambda checked: MCPServerState.toggle_server_active(
43
+ server.id, checked
44
+ ),
45
+ ),
46
+ white_space="nowrap",
47
+ ),
39
48
  rx.table.cell(
40
49
  rx.hstack(
41
50
  update_mcp_server_dialog(server),
@@ -62,8 +71,9 @@ def mcp_servers_table() -> rx.Fragment:
62
71
  rx.table.row(
63
72
  rx.table.column_header_cell("Name", width="20%"),
64
73
  rx.table.column_header_cell(
65
- "Beschreibung", width="calc(80% - 140px)"
74
+ "Beschreibung", width="calc(80% - 230px)"
66
75
  ),
76
+ rx.table.column_header_cell("Aktiv", width="90px"),
67
77
  rx.table.column_header_cell("", width="140px"),
68
78
  ),
69
79
  ),
@@ -1,7 +1,7 @@
1
1
  import reflex as rx
2
2
 
3
3
  import appkit_mantine as mn
4
- from appkit_assistant.backend.models import (
4
+ from appkit_assistant.backend.schemas import (
5
5
  Message,
6
6
  MessageType,
7
7
  Thinking,
@@ -150,6 +150,60 @@ class MessageActionsBar:
150
150
  )
151
151
 
152
152
 
153
+ def _render_annotation(ann: str) -> rx.Component:
154
+ return rx.cond(
155
+ ann.contains("http"),
156
+ rx.badge(
157
+ rx.link(
158
+ rx.hstack(
159
+ rx.icon("globe", size=12, flex_shrink=0),
160
+ rx.text(
161
+ ann,
162
+ style={
163
+ "overflow": "hidden",
164
+ "white_space": "nowrap",
165
+ "text_overflow": "ellipsis",
166
+ },
167
+ ),
168
+ align="center",
169
+ spacing="1",
170
+ width="100%",
171
+ ),
172
+ href=ann,
173
+ is_external=True,
174
+ size="1",
175
+ width="100%",
176
+ text_decoration="none",
177
+ style={"overflow": "hidden"},
178
+ ),
179
+ size="1",
180
+ variant="soft",
181
+ color_scheme="gray",
182
+ max_width="100%",
183
+ ),
184
+ rx.badge(
185
+ rx.hstack(
186
+ rx.icon("file-text", size=12, flex_shrink=0),
187
+ rx.text(
188
+ ann,
189
+ style={
190
+ "overflow": "hidden",
191
+ "white_space": "nowrap",
192
+ "text_overflow": "ellipsis",
193
+ },
194
+ ),
195
+ align="center",
196
+ spacing="1",
197
+ width="100%",
198
+ ),
199
+ size="1",
200
+ variant="soft",
201
+ color_scheme="gray",
202
+ max_width="100%",
203
+ ),
204
+ )
205
+
206
+
153
207
  class MessageComponent:
154
208
  @staticmethod
155
209
  def _file_badge(filename: str) -> rx.Component:
@@ -348,6 +402,37 @@ class MessageComponent:
348
402
  max_width="90%",
349
403
  ),
350
404
  ),
405
+ # Annotations/Sources
406
+ rx.cond(
407
+ message.annotations.length() > 0,
408
+ rx.vstack(
409
+ rx.hstack(
410
+ rx.icon("file-text", size=13, color=rx.color("gray", 9)),
411
+ rx.text(
412
+ "Quellen:",
413
+ size="1",
414
+ color=rx.color("gray", 9),
415
+ ),
416
+ spacing="1",
417
+ ),
418
+ rx.hstack(
419
+ rx.foreach(
420
+ message.annotations,
421
+ _render_annotation,
422
+ ),
423
+ spacing="2",
424
+ align="start",
425
+ flex_wrap="wrap",
426
+ max_width="95%",
427
+ ),
428
+ padding="6px",
429
+ margin_top="6px",
430
+ margin_bottom="6px",
431
+ width="90%",
432
+ align="start",
433
+ ),
434
+ rx.fragment(),
435
+ ),
351
436
  # Actions bar
352
437
  rx.cond(
353
438
  message.done,
@@ -482,7 +567,11 @@ class ToolCallComponent:
482
567
  return rx.cond(
483
568
  tool_item.type == ThinkingType.REASONING,
484
569
  ToolCallComponent._render_reasoning(tool_item),
485
- ToolCallComponent._render_tool_call(tool_item),
570
+ rx.cond(
571
+ tool_item.type == ThinkingType.PROCESSING,
572
+ ToolCallComponent._render_processing(tool_item),
573
+ ToolCallComponent._render_tool_call(tool_item),
574
+ ),
486
575
  )
487
576
 
488
577
  @staticmethod
@@ -494,6 +583,34 @@ class ToolCallComponent:
494
583
  margin_bottom="9px",
495
584
  )
496
585
 
586
+ @staticmethod
587
+ def _render_processing(item: Thinking) -> rx.Component:
588
+ """Render file processing progress."""
589
+ return rx.hstack(
590
+ rx.cond(
591
+ item.status == ThinkingStatus.COMPLETED,
592
+ rx.icon("circle-check", size=14, color=rx.color("green", 9)),
593
+ rx.cond(
594
+ item.status == ThinkingStatus.ERROR,
595
+ rx.icon("circle-x", size=14, color=rx.color("red", 9)),
596
+ rx.icon("file-up", size=14, color=rx.color("blue", 9)),
597
+ ),
598
+ ),
599
+ rx.text(
600
+ item.text,
601
+ size="1",
602
+ color=rx.cond(
603
+ item.status == ThinkingStatus.ERROR,
604
+ rx.color("red", 9),
605
+ rx.color("gray", 10),
606
+ ),
607
+ ),
608
+ spacing="2",
609
+ padding="3px 6px",
610
+ margin_bottom="3px",
611
+ width="100%",
612
+ )
613
+
497
614
  @staticmethod
498
615
  def _render_tool_call(item: Thinking) -> rx.Component:
499
616
  return rx.vstack(
@@ -4,7 +4,7 @@ from collections.abc import Callable
4
4
  import reflex as rx
5
5
 
6
6
  import appkit_mantine as mn
7
- from appkit_assistant.backend.models import Message, MessageType
7
+ from appkit_assistant.backend.schemas import Message, MessageType
8
8
  from appkit_assistant.components import composer
9
9
  from appkit_assistant.components.message import AuthCardComponent, MessageComponent
10
10
  from appkit_assistant.components.threadlist import ThreadList
@@ -1,6 +1,6 @@
1
1
  import reflex as rx
2
2
 
3
- from appkit_assistant.backend.models import ThreadModel
3
+ from appkit_assistant.backend.schemas import ThreadModel
4
4
  from appkit_assistant.state.thread_list_state import ThreadListState
5
5
  from appkit_assistant.state.thread_state import ThreadState
6
6
 
@@ -63,7 +63,9 @@ class ThreadList:
63
63
  margin_left="0px",
64
64
  margin_right="0px",
65
65
  color_scheme="gray",
66
- on_click=ThreadListState.delete_thread(thread.thread_id),
66
+ on_click=ThreadListState.delete_thread(
67
+ thread.thread_id
68
+ ).stop_propagation,
67
69
  ),
68
70
  content="Chat löschen",
69
71
  flex_shrink=0,
@@ -2,7 +2,7 @@
2
2
 
3
3
  import reflex as rx
4
4
 
5
- from appkit_assistant.backend.models import MCPServer
5
+ from appkit_assistant.backend.database.models import MCPServer
6
6
  from appkit_assistant.state.thread_state import ThreadState
7
7
 
8
8
 
@@ -29,19 +29,22 @@ def render_mcp_server_item(server: MCPServer) -> rx.Component:
29
29
  def tools_popover() -> rx.Component:
30
30
  """Render the tools modal popup."""
31
31
  return rx.popover.root(
32
- rx.popover.trigger(
33
- rx.button(
34
- rx.icon("pencil-ruler", size=18),
35
- rx.text(
36
- ThreadState.selected_mcp_servers.length().to_string()
37
- + " von "
38
- + ThreadState.available_mcp_servers.length().to_string(),
39
- size="1",
32
+ rx.tooltip(
33
+ rx.popover.trigger(
34
+ rx.button(
35
+ rx.icon("pencil-ruler", size=17),
36
+ rx.text(
37
+ ThreadState.selected_mcp_servers.length().to_string()
38
+ + " von "
39
+ + ThreadState.available_mcp_servers.length().to_string(),
40
+ size="1",
41
+ ),
42
+ cursor="pointer",
43
+ variant="ghost",
44
+ padding="8px",
40
45
  ),
41
- variant="ghost",
42
- size="2",
43
- border_radius="4px",
44
46
  ),
47
+ content="Werkzeuge verwalten",
45
48
  ),
46
49
  rx.popover.content(
47
50
  rx.vstack(
@@ -49,8 +52,7 @@ def tools_popover() -> rx.Component:
49
52
  rx.cond(
50
53
  ThreadState.available_mcp_servers.length() > 0,
51
54
  rx.text(
52
- "Wähle die Werkzeuge aus, die für diese Unterhaltung "
53
- "verfügbar sein sollen.",
55
+ "Wähle deine Werkzeuge für diese Unterhaltung aus.",
54
56
  size="2",
55
57
  color="gray",
56
58
  margin_bottom="1.5em",
@@ -73,16 +75,31 @@ def tools_popover() -> rx.Component:
73
75
  width="100%",
74
76
  ),
75
77
  width="100%",
76
- max_height="210px",
78
+ max_height="calc(66vh - 180px)",
77
79
  scrollbars="vertical",
78
80
  type="auto",
79
81
  ),
80
- rx.button(
81
- "Anwenden",
82
- on_click=ThreadState.apply_mcp_server_selection,
83
- variant="solid",
84
- color_scheme="blue",
82
+ rx.hstack(
83
+ rx.button(
84
+ "Anwenden",
85
+ on_click=ThreadState.apply_mcp_server_selection,
86
+ variant="solid",
87
+ color_scheme="blue",
88
+ ),
89
+ rx.tooltip(
90
+ rx.button(
91
+ rx.icon("paintbrush", size=17),
92
+ cursor="pointer",
93
+ variant="ghost",
94
+ padding="8px",
95
+ margin_left="6px",
96
+ on_click=ThreadState.deselect_all_mcp_servers,
97
+ ),
98
+ content="Alle abwählen",
99
+ ),
100
+ spacing="2",
85
101
  margin_top="1.5em",
102
+ align="center",
86
103
  ),
87
104
  spacing="1",
88
105
  ),
@@ -3,11 +3,23 @@ from pydantic import SecretStr
3
3
  from appkit_commons.configuration import BaseConfig
4
4
 
5
5
 
6
+ class FileUploadConfig(BaseConfig):
7
+ """Configuration for file upload and vector store management."""
8
+
9
+ vector_store_expiration_days: int = 2
10
+ max_file_size_mb: int = 50
11
+ max_files_per_thread: int = 10
12
+ cleanup_interval_minutes: int = 60
13
+ files_expiration_days: int = 30
14
+
15
+
6
16
  class AssistantConfig(BaseConfig):
7
17
  perplexity_api_key: SecretStr | None = None
8
18
  openai_base_url: str | None = None
9
19
  openai_api_key: SecretStr | None = None
20
+ openai_is_azure: bool = False
10
21
  claude_base_url: str | None = None
11
22
  claude_api_key: SecretStr | None = None
12
23
  google_api_key: SecretStr | None = None
13
24
  azure_ai_projects_endpoint: str | None = None
25
+ file_upload: FileUploadConfig = FileUploadConfig()