appkit-assistant 0.14.0__py3-none-any.whl → 0.15.0__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.
@@ -3,11 +3,12 @@
3
3
  import json
4
4
  import logging
5
5
  from collections.abc import AsyncGenerator
6
+ from datetime import UTC, datetime
6
7
  from typing import Any
7
8
 
8
9
  import reflex as rx
9
10
 
10
- from appkit_assistant.backend.models import MCPServer
11
+ from appkit_assistant.backend.models import MCPAuthType, MCPServer
11
12
  from appkit_assistant.backend.repositories import (
12
13
  mcp_server_repo,
13
14
  )
@@ -73,12 +74,32 @@ class MCPServerState(rx.State):
73
74
  """Add a new MCP server."""
74
75
  try:
75
76
  headers = self._parse_headers_from_form(form_data)
77
+ auth_type = form_data.get("auth_type", MCPAuthType.API_KEY)
78
+
76
79
  server_entity = MCPServer(
77
80
  name=form_data["name"],
78
81
  url=form_data["url"],
79
82
  headers=headers,
80
83
  description=form_data.get("description") or None,
81
84
  prompt=form_data.get("prompt") or None,
85
+ auth_type=auth_type,
86
+ oauth_client_id=(
87
+ form_data.get("oauth_client_id")
88
+ if auth_type == MCPAuthType.OAUTH_DISCOVERY
89
+ else None
90
+ ),
91
+ oauth_client_secret=(
92
+ form_data.get("oauth_client_secret")
93
+ if auth_type == MCPAuthType.OAUTH_DISCOVERY
94
+ else None
95
+ ),
96
+ oauth_issuer=form_data.get("oauth_issuer"),
97
+ oauth_authorize_url=form_data.get("oauth_authorize_url"),
98
+ oauth_token_url=form_data.get("oauth_token_url"),
99
+ oauth_scopes=form_data.get("oauth_scopes"),
100
+ oauth_discovered_at=(
101
+ datetime.now(UTC) if form_data.get("oauth_issuer") else None
102
+ ),
82
103
  )
83
104
 
84
105
  async with get_asyncdb_session() as session:
@@ -119,6 +140,7 @@ class MCPServerState(rx.State):
119
140
 
120
141
  try:
121
142
  headers = self._parse_headers_from_form(form_data)
143
+ auth_type = form_data.get("auth_type", MCPAuthType.API_KEY)
122
144
  updated_name = ""
123
145
 
124
146
  async with get_asyncdb_session() as session:
@@ -134,6 +156,25 @@ class MCPServerState(rx.State):
134
156
  existing_server.headers = headers
135
157
  existing_server.description = form_data.get("description") or None
136
158
  existing_server.prompt = form_data.get("prompt") or None
159
+ existing_server.auth_type = auth_type
160
+ existing_server.oauth_client_id = (
161
+ form_data.get("oauth_client_id")
162
+ if auth_type == MCPAuthType.OAUTH_DISCOVERY
163
+ else None
164
+ )
165
+ existing_server.oauth_client_secret = (
166
+ form_data.get("oauth_client_secret")
167
+ if auth_type == MCPAuthType.OAUTH_DISCOVERY
168
+ else None
169
+ )
170
+ existing_server.oauth_issuer = form_data.get("oauth_issuer")
171
+ existing_server.oauth_authorize_url = form_data.get(
172
+ "oauth_authorize_url"
173
+ )
174
+ existing_server.oauth_token_url = form_data.get("oauth_token_url")
175
+ existing_server.oauth_scopes = form_data.get("oauth_scopes")
176
+ if form_data.get("oauth_issuer"):
177
+ existing_server.oauth_discovered_at = datetime.now(UTC)
137
178
 
138
179
  updated_server = await mcp_server_repo.save(
139
180
  session, existing_server
@@ -76,7 +76,7 @@ class SystemPromptState(State):
76
76
  # Force textarea to re-render with loaded content
77
77
  self.textarea_key += 1
78
78
 
79
- logger.info("Loaded %s system prompt versions", len(self.versions))
79
+ logger.debug("Loaded %s system prompt versions", len(self.versions))
80
80
  except Exception as exc:
81
81
  self.error_message = f"Fehler beim Laden: {exc!s}"
82
82
  logger.exception("Failed to load system prompt versions")
@@ -115,12 +115,12 @@ class SystemPromptState(State):
115
115
 
116
116
  # Invalidate cache to force reload of new prompt version
117
117
  await invalidate_prompt_cache()
118
- logger.info("System prompt cache invalidated after save")
118
+ logger.debug("System prompt cache invalidated after save")
119
119
 
120
120
  await self.load_versions()
121
121
 
122
122
  yield rx.toast.success("Neue Version erfolgreich gespeichert.")
123
- logger.info("Saved new system prompt version by user %s", user_id)
123
+ logger.debug("Saved new system prompt version by user %s", user_id)
124
124
  except Exception as exc:
125
125
  self.error_message = f"Fehler beim Speichern: {exc!s}"
126
126
  logger.exception("Failed to save system prompt")
@@ -154,7 +154,7 @@ class SystemPromptState(State):
154
154
 
155
155
  # Invalidate cache since latest version might have changed
156
156
  await invalidate_prompt_cache()
157
- logger.info("System prompt cache invalidated after deletion")
157
+ logger.debug("System prompt cache invalidated after deletion")
158
158
 
159
159
  await self.load_versions()
160
160
  yield rx.toast.success("Version erfolgreich gelöscht.")
@@ -88,7 +88,7 @@ class ThreadListState(rx.State):
88
88
 
89
89
  # Handle user change
90
90
  if self._current_user_id != current_user_id:
91
- logger.info(
91
+ logger.debug(
92
92
  "User changed from '%s' to '%s' - resetting state",
93
93
  self._current_user_id or "(none)",
94
94
  current_user_id or "(none)",
@@ -265,7 +265,7 @@ class ThreadListState(rx.State):
265
265
  # Late import to avoid circular dependency
266
266
  from appkit_assistant.state.thread_state import ThreadState # noqa: PLC0415
267
267
 
268
- logger.info(
268
+ logger.debug(
269
269
  "Resetting ThreadListState on logout for user: %s",
270
270
  self._current_user_id,
271
271
  )
@@ -277,7 +277,7 @@ class ThreadListState(rx.State):
277
277
 
278
278
  # Reset ThreadState
279
279
  thread_state: ThreadState = await self.get_state(ThreadState)
280
- thread_state.new_thread()
280
+ await thread_state.new_thread()
281
281
 
282
282
  logger.debug("ThreadListState reset complete")
283
283
 
@@ -10,6 +10,7 @@ This module contains ThreadState which manages the current active thread:
10
10
  See thread_list_state.py for ThreadListState which manages the thread list sidebar.
11
11
  """
12
12
 
13
+ import json
13
14
  import logging
14
15
  import uuid
15
16
  from collections.abc import AsyncGenerator
@@ -98,17 +99,30 @@ class ThreadState(rx.State):
98
99
  temp_selected_mcp_servers: list[int] = []
99
100
  server_selection_state: dict[int, bool] = {}
100
101
 
102
+ # MCP OAuth state
103
+ pending_auth_server_id: str = ""
104
+ pending_auth_server_name: str = ""
105
+ pending_auth_url: str = ""
106
+ show_auth_card: bool = False
107
+ pending_oauth_message: str = "" # Message that triggered OAuth, resent on success
108
+
101
109
  # Thread list integration
102
110
  with_thread_list: bool = False
103
111
 
104
112
  # Internal state
105
113
  _initialized: bool = False
106
114
  _current_user_id: str = ""
115
+ _skip_user_message: bool = False # Skip adding user message (for OAuth resend)
107
116
 
108
117
  # -------------------------------------------------------------------------
109
118
  # Computed properties
110
119
  # -------------------------------------------------------------------------
111
120
 
121
+ @rx.var
122
+ def current_user_id(self) -> str:
123
+ """Get the current user ID for OAuth validation."""
124
+ return self._current_user_id
125
+
112
126
  @rx.var
113
127
  def get_selected_model(self) -> str:
114
128
  """Get the currently selected model ID."""
@@ -168,20 +182,44 @@ class ThreadState(rx.State):
168
182
  # -------------------------------------------------------------------------
169
183
 
170
184
  @rx.event
171
- def initialize(self) -> None:
185
+ async def initialize(self) -> None:
172
186
  """Initialize the state with models and a new empty thread.
173
187
 
174
188
  Only initializes once per user session. Resets when user changes.
175
189
  """
176
- # If already initialized, skip
177
- if self._initialized:
178
- logger.debug("Thread state already initialized")
190
+ user_session: UserSession = await self.get_state(UserSession)
191
+ user = await user_session.authenticated_user
192
+ current_user_id = str(user.user_id) if user else ""
193
+
194
+ # If already initialized and user hasn't changed, skip
195
+ if self._initialized and self._current_user_id == current_user_id:
196
+ logger.debug(
197
+ "Thread state already initialized for user %s", current_user_id
198
+ )
179
199
  return
180
200
 
181
201
  model_manager = ModelManager()
182
- self.ai_models = model_manager.get_all_models()
202
+ all_models = model_manager.get_all_models()
183
203
  self.selected_model = model_manager.get_default_model()
184
204
 
205
+ # Filter models based on user roles
206
+ user_roles = user.roles if user else []
207
+
208
+ self.ai_models = [
209
+ m
210
+ for m in all_models
211
+ if not m.requires_role or m.requires_role in user_roles
212
+ ]
213
+
214
+ # If selected model is not in available models, pick the first one
215
+ available_model_ids = [m.id for m in self.ai_models]
216
+ if self.selected_model not in available_model_ids:
217
+ if available_model_ids:
218
+ self.selected_model = available_model_ids[0]
219
+ else:
220
+ logger.warning("No models available for user")
221
+ self.selected_model = ""
222
+
185
223
  self._thread = ThreadModel(
186
224
  thread_id=str(uuid.uuid4()),
187
225
  title="Neuer Chat",
@@ -196,11 +234,12 @@ class ThreadState(rx.State):
196
234
  self.image_chunks = []
197
235
  self.prompt = ""
198
236
  self.show_thinking = False
237
+ self._current_user_id = current_user_id
199
238
  self._initialized = True
200
239
  logger.debug("Initialized thread state: %s", self._thread.thread_id)
201
240
 
202
241
  @rx.event
203
- def new_thread(self) -> None:
242
+ async def new_thread(self) -> None:
204
243
  """Create a new empty thread (not persisted, not in list yet).
205
244
 
206
245
  Called when user clicks "New Chat" or when active thread is deleted.
@@ -208,7 +247,7 @@ class ThreadState(rx.State):
208
247
  """
209
248
  # Ensure state is initialized first
210
249
  if not self._initialized:
211
- self.initialize()
250
+ await self.initialize()
212
251
 
213
252
  # Don't create new if current thread is already empty
214
253
  if self._thread.state == ThreadStatus.NEW and not self.messages:
@@ -277,8 +316,6 @@ class ThreadState(rx.State):
277
316
 
278
317
  if not thread_entity:
279
318
  logger.warning("Thread %s not found in database", thread_id)
280
- # We can't access self in here easily to clear loading state unless we break out
281
- # but we can check thread_entity after context
282
319
 
283
320
  # Convert to ThreadModel if found
284
321
  full_thread = None
@@ -292,14 +329,13 @@ class ThreadState(rx.State):
292
329
  messages=[Message(**m) for m in thread_entity.messages],
293
330
  )
294
331
 
295
- if not full_thread:
296
- if not thread_entity: # it was not found
297
- async with self:
298
- threadlist_state: ThreadListState = await self.get_state(
299
- ThreadListState
300
- )
301
- threadlist_state.loading_thread_id = ""
302
- return
332
+ if not full_thread and not thread_entity: # it was not found
333
+ async with self:
334
+ threadlist_state: ThreadListState = await self.get_state(
335
+ ThreadListState
336
+ )
337
+ threadlist_state.loading_thread_id = ""
338
+ return
303
339
 
304
340
  # Mark all messages as done (loaded from DB)
305
341
  if full_thread:
@@ -347,9 +383,15 @@ class ThreadState(rx.State):
347
383
  self.prompt = prompt
348
384
 
349
385
  @rx.event
350
- def set_suggestions(self, suggestions: list[Suggestion]) -> None:
351
- """Set custom suggestions for the thread."""
352
- self.suggestions = suggestions
386
+ def set_suggestions(self, suggestions: list[Suggestion] | list[dict]) -> None:
387
+ """Set custom suggestions for the thread.
388
+
389
+ Accepts either Suggestion objects or dicts (for Reflex serialization).
390
+ """
391
+ if suggestions and isinstance(suggestions[0], dict):
392
+ self.suggestions = [Suggestion(**s) for s in suggestions]
393
+ else:
394
+ self.suggestions = suggestions # type: ignore[assignment]
353
395
 
354
396
  @rx.event
355
397
  def set_selected_model(self, model_id: str) -> None:
@@ -465,12 +507,17 @@ class ThreadState(rx.State):
465
507
  )
466
508
  return
467
509
 
510
+ async with self:
511
+ user_session: UserSession = await self.get_state(UserSession)
512
+ user_id = user_session.user.user_id if user_session.user else None
513
+
468
514
  first_response_received = False
469
515
  try:
470
516
  async for chunk in processor.process(
471
517
  self.messages,
472
518
  selected_model,
473
519
  mcp_servers=mcp_servers,
520
+ user_id=user_id,
474
521
  ):
475
522
  first_response_received = await self._handle_stream_chunk(
476
523
  chunk=chunk,
@@ -508,12 +555,16 @@ class ThreadState(rx.State):
508
555
  self.prompt = ""
509
556
 
510
557
  is_new_thread = self._thread.state == ThreadStatus.NEW
511
- self.messages.extend(
512
- [
513
- Message(text=current_prompt, type=MessageType.HUMAN),
514
- Message(text="", type=MessageType.ASSISTANT),
515
- ]
516
- )
558
+
559
+ # Add user message unless skipped (e.g., OAuth resend)
560
+ if self._skip_user_message:
561
+ self._skip_user_message = False
562
+ else:
563
+ self.messages.append(
564
+ Message(text=current_prompt, type=MessageType.HUMAN)
565
+ )
566
+ # Always add assistant placeholder
567
+ self.messages.append(Message(text="", type=MessageType.ASSISTANT))
517
568
 
518
569
  selected_model = self.get_selected_model
519
570
  if not selected_model:
@@ -561,7 +612,9 @@ class ThreadState(rx.State):
561
612
  """Finalize state after a successful full response."""
562
613
  async with self:
563
614
  self.show_thinking = False
564
- self._thread.messages = self.messages
615
+ # Convert Reflex proxy list to standard list to avoid Pydantic
616
+ # serializer warnings
617
+ self._thread.messages = list(self.messages) # noqa: E501
565
618
  self._thread.ai_model = self.selected_model
566
619
 
567
620
  if self.with_thread_list:
@@ -588,7 +641,9 @@ class ThreadState(rx.State):
588
641
  self._thread.title = current_prompt[:100]
589
642
  await self._notify_thread_created()
590
643
 
591
- self._thread.messages = self.messages
644
+ # Convert Reflex proxy list to standard list to avoid Pydantic serializer
645
+ # warnings
646
+ self._thread.messages = list(self.messages) # noqa: E501
592
647
  if self.with_thread_list:
593
648
  await self._save_thread_to_db()
594
649
 
@@ -716,6 +771,8 @@ class ThreadState(rx.State):
716
771
  elif chunk.type == ChunkType.COMPLETION:
717
772
  self.show_thinking = False
718
773
  logger.debug("Response generation completed")
774
+ elif chunk.type == ChunkType.AUTH_REQUIRED:
775
+ self._handle_auth_required_chunk(chunk)
719
776
  elif chunk.type == ChunkType.ERROR:
720
777
  self.messages.append(Message(text=chunk.text, type=MessageType.ERROR))
721
778
  logger.error("Chunk error: %s", chunk.text)
@@ -844,6 +901,111 @@ class ThreadState(rx.State):
844
901
 
845
902
  self.thinking_items = self.thinking_items.copy()
846
903
 
904
+ def _handle_auth_required_chunk(self, chunk: Chunk) -> None:
905
+ """Handle AUTH_REQUIRED chunks by showing the auth card."""
906
+ self.pending_auth_server_id = chunk.chunk_metadata.get("server_id", "")
907
+ self.pending_auth_server_name = chunk.chunk_metadata.get("server_name", "")
908
+ self.pending_auth_url = chunk.chunk_metadata.get("auth_url", "")
909
+ self.show_auth_card = True
910
+ # Store the last user message to resend after successful OAuth
911
+ for msg in reversed(self.messages):
912
+ if msg.type == MessageType.HUMAN:
913
+ self.pending_oauth_message = msg.text
914
+ break
915
+ logger.debug(
916
+ "Auth required for server %s, showing auth card, pending message: %s",
917
+ self.pending_auth_server_name,
918
+ self.pending_oauth_message[:50] if self.pending_oauth_message else "None",
919
+ )
920
+
921
+ @rx.event
922
+ def start_mcp_oauth(self) -> rx.event.EventSpec:
923
+ """Start the OAuth flow by opening the auth URL in a popup window."""
924
+ if not self.pending_auth_url:
925
+ return rx.toast.error("Keine Authentifizierungs-URL verfügbar")
926
+
927
+ # NOTE: We do not append server_id here anymore to avoid errors with strict
928
+ # OAuth providers. server_id must be recovered from the state parameter in the
929
+ # callback.
930
+ auth_url = self.pending_auth_url
931
+
932
+ return rx.call_script(
933
+ f"window.open('{auth_url}', 'mcp_oauth', 'width=600,height=700')"
934
+ )
935
+
936
+ @rx.event
937
+ async def handle_mcp_oauth_success(
938
+ self, server_id: str, server_name: str
939
+ ) -> AsyncGenerator[Any, Any]:
940
+ """Handle successful OAuth completion from popup window."""
941
+ logger.debug("OAuth success for server %s (%s)", server_name, server_id)
942
+ self.show_auth_card = False
943
+ self.pending_auth_server_id = ""
944
+ self.pending_auth_server_name = ""
945
+ self.pending_auth_url = ""
946
+
947
+ # Check if we have a pending message to resend
948
+ pending_message = self.pending_oauth_message
949
+ self.pending_oauth_message = ""
950
+
951
+ if pending_message:
952
+ # Remove the incomplete assistant message from the failed attempt
953
+ if self.messages and self.messages[-1].type == MessageType.ASSISTANT:
954
+ self.messages = self.messages[:-1]
955
+ # Add success message
956
+ self.messages.append(
957
+ Message(
958
+ text=f"Erfolgreich mit {server_name} verbunden. "
959
+ "Anfrage wird erneut gesendet...",
960
+ type=MessageType.INFO,
961
+ )
962
+ )
963
+ # Resend the original message by setting prompt and yielding the event
964
+ self.prompt = pending_message
965
+ self._skip_user_message = True # User message already in list
966
+ yield ThreadState.submit_message
967
+ else:
968
+ # No pending message - just show success
969
+ self.messages.append(
970
+ Message(
971
+ text=f"Erfolgreich mit {server_name} verbunden.",
972
+ type=MessageType.INFO,
973
+ )
974
+ )
975
+
976
+ @rx.event
977
+ def handle_mcp_oauth_success_from_js(self) -> rx.event.EventSpec:
978
+ """Handle OAuth success triggered from JS - retrieves data from window."""
979
+ return rx.call_script(
980
+ "window._mcpOAuthData ? JSON.stringify(window._mcpOAuthData) : '{}'",
981
+ callback=ThreadState.process_oauth_success_data,
982
+ )
983
+
984
+ @rx.event
985
+ async def process_oauth_success_data(
986
+ self, data_str: str
987
+ ) -> AsyncGenerator[Any, Any]:
988
+ """Process OAuth success data retrieved from window."""
989
+ try:
990
+ data = json.loads(data_str) if data_str else {}
991
+ server_id = data.get("serverId", "")
992
+ server_name = data.get("serverName", "Unknown")
993
+ logger.info(
994
+ "Processing OAuth success from JS: server_id=%s, server_name=%s",
995
+ server_id,
996
+ server_name,
997
+ )
998
+ # Yield events from handle_mcp_oauth_success
999
+ async for event in self.handle_mcp_oauth_success(server_id, server_name):
1000
+ yield event
1001
+ except json.JSONDecodeError:
1002
+ logger.warning("Failed to parse OAuth data from JS: %s", data_str)
1003
+
1004
+ @rx.event
1005
+ def dismiss_auth_card(self) -> None:
1006
+ """Dismiss the auth card without authenticating."""
1007
+ self.show_auth_card = False
1008
+
847
1009
  def _add_error_message(self, error_msg: str) -> None:
848
1010
  """Add an error message to the conversation."""
849
1011
  logger.error(error_msg)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: appkit-assistant
3
- Version: 0.14.0
3
+ Version: 0.15.0
4
4
  Summary: Add your description here
5
5
  Project-URL: Homepage, https://github.com/jenreh/appkit
6
6
  Project-URL: Documentation, https://github.com/jenreh/appkit/tree/main/docs
@@ -0,0 +1,29 @@
1
+ appkit_assistant/configuration.py,sha256=3nBL-dEGYsvSnRDNpxtikZn4QMMkMlNbb4VqGOPolJI,346
2
+ appkit_assistant/backend/mcp_auth_service.py,sha256=lYQEe4yOZ48ear6dvcuOXsaOc6RClIBMsOOkV7SG5Aw,27768
3
+ appkit_assistant/backend/model_manager.py,sha256=fmv3yP63LxDnne4vjT7IzETTI2aSxViC2FSUfHQajlk,4382
4
+ appkit_assistant/backend/models.py,sha256=y9pjoHMtkaGTO1CVrjcc3T4FoOn3ROnaust9c3OIaVQ,7488
5
+ appkit_assistant/backend/processor.py,sha256=dhBg3pYXdmpj9JtAJc-d83SeUA1NsICj1C_YI0M2QYE,1289
6
+ appkit_assistant/backend/repositories.py,sha256=R-7kYdxg4RWQrTEOU4tbcOEhJA_FlesWrt65UpItRSU,5547
7
+ appkit_assistant/backend/system_prompt_cache.py,sha256=83OIyixeTb3HKOy3XIzPyTAE-G2JyqrfcG8xVeTS2Ls,5514
8
+ appkit_assistant/backend/processors/lorem_ipsum_processor.py,sha256=j-MZhzibrtabzbGB2Pf4Xcdlr1TlTYWNRdE22LsDp9Q,4635
9
+ appkit_assistant/backend/processors/openai_base.py,sha256=IQS4m375BOD_K0PBFOk4i7wL1z5MEiPFxbSmC-HBNgU,4414
10
+ appkit_assistant/backend/processors/openai_chat_completion_processor.py,sha256=nTxouoXDU6VcQr8UhA2KiMNt60KvIwM8cH9Z8lo4dXY,4218
11
+ appkit_assistant/backend/processors/openai_responses_processor.py,sha256=MWBZsvtqE-knviY_-3km9zgmm7WPno5WDBXcbZHOZjE,28360
12
+ appkit_assistant/backend/processors/perplexity_processor.py,sha256=weHukv78MSCF_uSCKGSMpNYHsET9OB8IhpvUiMfPQ8A,3355
13
+ appkit_assistant/components/__init__.py,sha256=5tzK5VjX9FGKK-qTUHLjr8-ohT4ykb4a-zC-I3yeRLY,916
14
+ appkit_assistant/components/composer.py,sha256=F4VPxWp4P6fvTW4rQ7S-YWn0eje5c3jGsWrpC1aewss,3885
15
+ appkit_assistant/components/composer_key_handler.py,sha256=KyZYyhxzFR8DH_7F_DrvTFNT6v5kG6JihlGTmCv2wv0,1028
16
+ appkit_assistant/components/mcp_server_dialogs.py,sha256=afIImmhfrNyLmxDZBpCxHxvD8HKpDanIloLEC8dJgro,23444
17
+ appkit_assistant/components/mcp_server_table.py,sha256=1dziN7hDDvE8Y3XcdIs0wUPv1H64kP9gRAEjgH9Yvzo,2323
18
+ appkit_assistant/components/message.py,sha256=gbHp3VJmOTS6aF4QOhNhCrvh5ra38UhJxVTLtY8_o6A,11927
19
+ appkit_assistant/components/system_prompt_editor.py,sha256=REl33zFmcpYRe9kxvFrBRYg40dV4L4FtVC_3ibLsmrU,2940
20
+ appkit_assistant/components/thread.py,sha256=Hyb09R_zsRUzFXN4a2ndkbH-EvHy9Pnl-Q3y6-djjP0,12660
21
+ appkit_assistant/components/threadlist.py,sha256=1xVakSTQYi5-wgED3fTJVggeIjL_fkthehce0wKUYtM,4896
22
+ appkit_assistant/components/tools_modal.py,sha256=12iiAVahy3j4JwjGfRlegVEa4ePhGsEu7Bq92JLn1ZI,3353
23
+ appkit_assistant/state/mcp_server_state.py,sha256=3AFvy53xx_eLTxw-LfJklPTgq4Ohqu4xs1QlLs-kU4U,11387
24
+ appkit_assistant/state/system_prompt_state.py,sha256=zdnYrTnl7EszALRiodu6pcuQUd2tmtPG1eJ10j_OotI,7705
25
+ appkit_assistant/state/thread_list_state.py,sha256=j_juff-I1PQfvVTDF8OVTp5kiw1kuhyOX9eXDoCBKs0,10238
26
+ appkit_assistant/state/thread_state.py,sha256=3LNnEmdp6oU8npt1MEUBYA31cdF-QAP_-Nfh7sVCEFQ,38735
27
+ appkit_assistant-0.15.0.dist-info/METADATA,sha256=CF3zVOvepmSBULI1nv-4BmP8eRSfbjYvtGZ4CbwMEZE,9403
28
+ appkit_assistant-0.15.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
29
+ appkit_assistant-0.15.0.dist-info/RECORD,,
@@ -1,28 +0,0 @@
1
- appkit_assistant/configuration.py,sha256=3nBL-dEGYsvSnRDNpxtikZn4QMMkMlNbb4VqGOPolJI,346
2
- appkit_assistant/backend/model_manager.py,sha256=7f65UbZ51qOYM6F73eKbZ8hMnCzxdanFZKgdKF8bbCk,4366
3
- appkit_assistant/backend/models.py,sha256=-sr10xChq_lMlLpt7Jbmq4VGweuW4_UUwYsRtIY7HFY,6015
4
- appkit_assistant/backend/processor.py,sha256=dhBg3pYXdmpj9JtAJc-d83SeUA1NsICj1C_YI0M2QYE,1289
5
- appkit_assistant/backend/repositories.py,sha256=arYagqYDgn65uFvnFTXP6zaXOn1c3zoAYuPO69-qrSk,5546
6
- appkit_assistant/backend/system_prompt_cache.py,sha256=4a0sX3qzz3XTF7MX4YJuH29eaekmEx6jciY1zDKcuQ8,5509
7
- appkit_assistant/backend/processors/lorem_ipsum_processor.py,sha256=j-MZhzibrtabzbGB2Pf4Xcdlr1TlTYWNRdE22LsDp9Q,4635
8
- appkit_assistant/backend/processors/openai_base.py,sha256=IQS4m375BOD_K0PBFOk4i7wL1z5MEiPFxbSmC-HBNgU,4414
9
- appkit_assistant/backend/processors/openai_chat_completion_processor.py,sha256=nTxouoXDU6VcQr8UhA2KiMNt60KvIwM8cH9Z8lo4dXY,4218
10
- appkit_assistant/backend/processors/openai_responses_processor.py,sha256=Ns8owrvimtZofyyzhoTgi2t_P0feEgLzooJVfCxC3kw,18800
11
- appkit_assistant/backend/processors/perplexity_processor.py,sha256=weHukv78MSCF_uSCKGSMpNYHsET9OB8IhpvUiMfPQ8A,3355
12
- appkit_assistant/components/__init__.py,sha256=5tzK5VjX9FGKK-qTUHLjr8-ohT4ykb4a-zC-I3yeRLY,916
13
- appkit_assistant/components/composer.py,sha256=F4VPxWp4P6fvTW4rQ7S-YWn0eje5c3jGsWrpC1aewss,3885
14
- appkit_assistant/components/composer_key_handler.py,sha256=KyZYyhxzFR8DH_7F_DrvTFNT6v5kG6JihlGTmCv2wv0,1028
15
- appkit_assistant/components/mcp_server_dialogs.py,sha256=00BL6xdHeWcW1MMGJrmUFjiZ-Ksa22L-lydqPihZJwk,11451
16
- appkit_assistant/components/mcp_server_table.py,sha256=1dziN7hDDvE8Y3XcdIs0wUPv1H64kP9gRAEjgH9Yvzo,2323
17
- appkit_assistant/components/message.py,sha256=Tr19CsRqKiMP_fhSByVlUtigeXF13duR6Rp2mQ08IeQ,9636
18
- appkit_assistant/components/system_prompt_editor.py,sha256=REl33zFmcpYRe9kxvFrBRYg40dV4L4FtVC_3ibLsmrU,2940
19
- appkit_assistant/components/thread.py,sha256=rYM4LA6PVdEvZ5oz5ZtheVfQfFuvTXTIskTt15kI1kg,7886
20
- appkit_assistant/components/threadlist.py,sha256=1xVakSTQYi5-wgED3fTJVggeIjL_fkthehce0wKUYtM,4896
21
- appkit_assistant/components/tools_modal.py,sha256=12iiAVahy3j4JwjGfRlegVEa4ePhGsEu7Bq92JLn1ZI,3353
22
- appkit_assistant/state/mcp_server_state.py,sha256=DJJ9LVIMk-RXio-BbeZv6J9eShqg_dNANDdINkZECpc,9287
23
- appkit_assistant/state/system_prompt_state.py,sha256=iwjz3ABr1GGFau7sTP3c-5vfc0VYer_1mPbBNhOZsys,7701
24
- appkit_assistant/state/thread_list_state.py,sha256=uDWPJ82ZqSSj8jos1P2NmbRSm-BZz5xwenl9aMzrpHo,10230
25
- appkit_assistant/state/thread_state.py,sha256=8nAALYOa55GVoonVcf57rusUcsVEatn-zcofeLdBtfk,31882
26
- appkit_assistant-0.14.0.dist-info/METADATA,sha256=pG1dZK1hqKJf-suHF4nAnq9kJUw8bbqMyJiUIyV1sXg,9403
27
- appkit_assistant-0.14.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
28
- appkit_assistant-0.14.0.dist-info/RECORD,,