flock-core 0.4.0b48__py3-none-any.whl → 0.4.0b49__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.

Potentially problematic release.


This version of flock-core might be problematic. Click here for more details.

@@ -0,0 +1,156 @@
1
+ import logging
2
+ import sqlite3
3
+ from abc import ABC, abstractmethod
4
+ from pathlib import Path
5
+
6
+ import aiosqlite
7
+
8
+ from flock.webapp.app.services.sharing_models import SharedLinkConfig
9
+
10
+ # Get a logger instance
11
+ logger = logging.getLogger(__name__)
12
+
13
+ class SharedLinkStoreInterface(ABC):
14
+ """Interface for storing and retrieving shared link configurations."""
15
+
16
+ @abstractmethod
17
+ async def initialize(self) -> None:
18
+ """Initialize the store (e.g., create tables)."""
19
+ pass
20
+
21
+ @abstractmethod
22
+ async def save_config(self, config: SharedLinkConfig) -> SharedLinkConfig:
23
+ """Saves a shared link configuration."""
24
+ pass
25
+
26
+ @abstractmethod
27
+ async def get_config(self, share_id: str) -> SharedLinkConfig | None:
28
+ """Retrieves a shared link configuration by its ID."""
29
+ pass
30
+
31
+ @abstractmethod
32
+ async def delete_config(self, share_id: str) -> bool:
33
+ """Deletes a shared link configuration by its ID. Returns True if deleted, False otherwise."""
34
+ pass
35
+
36
+ class SQLiteSharedLinkStore(SharedLinkStoreInterface):
37
+ """SQLite implementation for storing and retrieving shared link configurations."""
38
+
39
+ def __init__(self, db_path: str):
40
+ self.db_path = Path(db_path)
41
+ self.db_path.parent.mkdir(parents=True, exist_ok=True) # Ensure directory exists
42
+ logger.info(f"SQLiteSharedLinkStore initialized with db_path: {self.db_path}")
43
+
44
+ async def initialize(self) -> None:
45
+ """Initializes the database and creates/updates the table if it doesn't exist."""
46
+ try:
47
+ async with aiosqlite.connect(self.db_path) as db:
48
+ # Ensure the table exists with the base schema first
49
+ await db.execute(
50
+ """
51
+ CREATE TABLE IF NOT EXISTS shared_links (
52
+ share_id TEXT PRIMARY KEY,
53
+ agent_name TEXT NOT NULL,
54
+ flock_definition TEXT NOT NULL,
55
+ created_at TEXT NOT NULL
56
+ /* New columns will be added below if they don't exist */
57
+ )
58
+ """
59
+ )
60
+
61
+ # Add new columns individually, ignoring errors if they already exist
62
+ new_columns = [
63
+ ("share_type", "TEXT DEFAULT 'agent_run' NOT NULL"),
64
+ ("chat_message_key", "TEXT"),
65
+ ("chat_history_key", "TEXT"),
66
+ ("chat_response_key", "TEXT")
67
+ ]
68
+
69
+ for column_name, column_type in new_columns:
70
+ try:
71
+ await db.execute(f"ALTER TABLE shared_links ADD COLUMN {column_name} {column_type}")
72
+ logger.info(f"Added column '{column_name}' to shared_links table.")
73
+ except sqlite3.OperationalError as e:
74
+ if "duplicate column name" in str(e).lower():
75
+ logger.debug(f"Column '{column_name}' already exists in shared_links table.")
76
+ else:
77
+ raise # Re-raise if it's a different operational error
78
+
79
+ await db.commit()
80
+ logger.info(f"Database initialized and shared_links table schema ensured at {self.db_path}")
81
+ except sqlite3.Error as e:
82
+ logger.error(f"SQLite error during initialization: {e}", exc_info=True)
83
+ raise
84
+
85
+ async def save_config(self, config: SharedLinkConfig) -> SharedLinkConfig:
86
+ """Saves a shared link configuration to the SQLite database."""
87
+ try:
88
+ async with aiosqlite.connect(self.db_path) as db:
89
+ await db.execute(
90
+ """INSERT INTO shared_links (
91
+ share_id, agent_name, created_at, flock_definition,
92
+ share_type, chat_message_key, chat_history_key, chat_response_key
93
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
94
+ (
95
+ config.share_id,
96
+ config.agent_name,
97
+ config.created_at.isoformat(),
98
+ config.flock_definition,
99
+ config.share_type,
100
+ config.chat_message_key,
101
+ config.chat_history_key,
102
+ config.chat_response_key,
103
+ ),
104
+ )
105
+ await db.commit()
106
+ logger.info(f"Saved shared link config for ID: {config.share_id} with type: {config.share_type}")
107
+ return config
108
+ except sqlite3.Error as e:
109
+ logger.error(f"SQLite error saving config for ID {config.share_id}: {e}", exc_info=True)
110
+ raise
111
+
112
+ async def get_config(self, share_id: str) -> SharedLinkConfig | None:
113
+ """Retrieves a shared link configuration from SQLite by its ID."""
114
+ try:
115
+ async with aiosqlite.connect(self.db_path) as db:
116
+ async with db.execute(
117
+ """SELECT
118
+ share_id, agent_name, created_at, flock_definition,
119
+ share_type, chat_message_key, chat_history_key, chat_response_key
120
+ FROM shared_links WHERE share_id = ?""",
121
+ (share_id,)
122
+ ) as cursor:
123
+ row = await cursor.fetchone()
124
+ if row:
125
+ logger.debug(f"Retrieved shared link config for ID: {share_id}")
126
+ return SharedLinkConfig(
127
+ share_id=row[0],
128
+ agent_name=row[1],
129
+ created_at=row[2], # SQLite stores as TEXT, Pydantic will parse from ISO format
130
+ flock_definition=row[3],
131
+ share_type=row[4],
132
+ chat_message_key=row[5],
133
+ chat_history_key=row[6],
134
+ chat_response_key=row[7],
135
+ )
136
+ logger.debug(f"No shared link config found for ID: {share_id}")
137
+ return None
138
+ except sqlite3.Error as e:
139
+ logger.error(f"SQLite error retrieving config for ID {share_id}: {e}", exc_info=True)
140
+ return None # Or raise, depending on desired error handling
141
+
142
+ async def delete_config(self, share_id: str) -> bool:
143
+ """Deletes a shared link configuration from SQLite by its ID."""
144
+ try:
145
+ async with aiosqlite.connect(self.db_path) as db:
146
+ result = await db.execute("DELETE FROM shared_links WHERE share_id = ?", (share_id,))
147
+ await db.commit()
148
+ deleted_count = result.rowcount
149
+ if deleted_count > 0:
150
+ logger.info(f"Deleted shared link config for ID: {share_id}")
151
+ return True
152
+ logger.info(f"Attempted to delete non-existent shared link config for ID: {share_id}")
153
+ return False
154
+ except sqlite3.Error as e:
155
+ logger.error(f"SQLite error deleting config for ID {share_id}: {e}", exc_info=True)
156
+ return False # Or raise
@@ -240,3 +240,60 @@ body:not(.chat-page) .chat-settings-form .grid button:first-child {
240
240
  main.main-content:has(#chat-container) {
241
241
  overflow: hidden;
242
242
  }
243
+
244
+ /* Ensure all direct text and common markdown elements within bot bubbles use the bot's text color */
245
+ .bubble.bot,
246
+ .bubble.bot p,
247
+ .bubble.bot li,
248
+ .bubble.bot h1,
249
+ .bubble.bot h2,
250
+ .bubble.bot h3,
251
+ .bubble.bot h4,
252
+ .bubble.bot h5,
253
+ .bubble.bot h6,
254
+ .bubble.bot strong,
255
+ .bubble.bot em,
256
+ .bubble.bot table,
257
+ .bubble.bot th,
258
+ .bubble.bot td {
259
+ color: var(--pico-code-color);
260
+ }
261
+
262
+ /* For links specifically within bot messages, you might want them to also use the bot text color */
263
+ .bubble.bot a {
264
+ color: var(--pico-code-color);
265
+ text-decoration: underline; /* Or your preferred link style */
266
+ }
267
+ .bubble.bot a:hover {
268
+ color: var(--pico-primary-hover, var(--pico-primary));
269
+ text-decoration: underline;
270
+ }
271
+
272
+ /* Styling for code blocks generated by Markdown and highlighted by Prism.js */
273
+ /* The prism-okaidia theme will handle the internal colors of the code. */
274
+ /* This is more about the container of the code block. */
275
+ .bubble.bot pre[class*="language-"] {
276
+ background-color: var(--pico-card-background-color); /* Or a slightly different dark shade */
277
+ border: 1px solid var(--pico-muted-border-color);
278
+ border-radius: var(--pico-border-radius);
279
+ padding: 0.75em;
280
+ margin: 0.5em 0;
281
+ font-size: 0.875em; /* Adjust as needed */
282
+ overflow-x: auto; /* Allow horizontal scrolling for long code lines */
283
+ /* The text color *inside* the code block will be handled by the Prism theme (e.g., Okaidia) */
284
+ }
285
+
286
+ /* Ensure the code itself inside the pre block also resets its base color if needed,
287
+ though Prism themes usually take care of this. This is a fallback. */
288
+ .bubble.bot pre[class*="language-"] code {
289
+ /* color: inherit; */ /* This might not be necessary if Prism theme is comprehensive - try without first */
290
+ background: none;
291
+ padding: 0;
292
+ }
293
+
294
+ /* Styles for preformatted text wrapping */
295
+ .bubble pre {
296
+ white-space: pre-wrap; /* Allows wrapping and preserves sequences of white space and newlines */
297
+ word-wrap: break-word; /* Breaks long words/strings if they would overflow */
298
+ overflow-x: auto; /* Adds a scrollbar if content is still too wide */
299
+ }
@@ -6,6 +6,8 @@
6
6
  <title>Flock Chat</title>
7
7
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css" />
8
8
  <link rel="stylesheet" href="/static/css/chat.css">
9
+ {# Prism.js CSS for syntax highlighting (okaidia theme) #}
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css" referrerpolicy="no-referrer" />
9
11
  {# Inject active theme variables #}
10
12
  {% if theme_css %}
11
13
  <style>
@@ -17,6 +19,9 @@
17
19
  </style>
18
20
  {% endif %}
19
21
  <script src="https://unpkg.com/htmx.org@1.9.10" integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC" crossorigin="anonymous"></script>
22
+ {# Prism.js JS (core and components for common languages) #}
23
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js" referrerpolicy="no-referrer"></script>
24
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js" referrerpolicy="no-referrer"></script>
20
25
  </head>
21
26
  <body class="chat-page">
22
27
  <div id="chat-container">
@@ -25,21 +30,33 @@
25
30
  <h2>Flock Chat</h2>
26
31
  <h3>{{ chat_subtitle }}</h3>
27
32
  </hgroup>
28
- <button class="secondary outline" hx-get="/chat/htmx/settings-form" hx-target="#chat-content-area" hx-swap="innerHTML" style="min-width:auto;">Settings</button>
33
+ {% if not is_shared_chat %}
34
+ <button class="secondary outline" hx-get="/chat/settings-standalone" hx-target="#chat-content-area" hx-swap="innerHTML" style="min-width:auto;">Settings</button>
35
+ {% else %}
36
+ {# Optionally, could show a disabled settings button or some other indicator #}
37
+ <span style="font-size: 0.8rem; color: var(--pico-muted-color);">Settings frozen for shared session.</span>
38
+ {% endif %}
29
39
  </div>
30
40
 
31
41
  <div id="chat-content-area">
32
- <div id="chat-log" hx-get="/chat/messages" hx-trigger="load" hx-swap="innerHTML">
42
+ <div id="chat-log"
43
+ hx-get="{% if is_shared_chat %}/chat/messages-shared/{{ share_id }}{% else %}/chat/messages{% endif %}"
44
+ hx-trigger="load" {# Polling removed #}
45
+ hx-swap="innerHTML">
33
46
  <p><em>Loading chat…</em></p>
34
47
  </div>
35
48
 
36
49
  <form id="chat-form-standalone"
37
- hx-post="/chat/send"
50
+ hx-post="{% if is_shared_chat %}/chat/send-shared{% else %}/chat/send{% endif %}"
38
51
  hx-target="#chat-log"
39
52
  hx-swap="innerHTML"
40
53
  hx-disabled-elt="input[name='message'], button[type='submit']"
41
54
  hx-on::before-request="htmx.find('#chat-form-standalone button[type=\'submit\']').textContent = 'Sending...'"
42
55
  hx-on::after-request="htmx.find('#chat-form-standalone button[type=\'submit\']').textContent = 'Send'; this.reset();">
56
+
57
+ {% if is_shared_chat %}
58
+ <input type="hidden" name="share_id" value="{{ share_id }}">
59
+ {% endif %}
43
60
  <input type="text" name="message" placeholder="Type a message…" required autofocus>
44
61
  <button type="submit">Send</button>
45
62
  </form>
@@ -57,9 +74,17 @@
57
74
  log.scrollTop = log.scrollHeight;
58
75
  }
59
76
  document.addEventListener('htmx:afterSwap', e => {
60
- if (e.detail.target.id === 'chat-log') scrollBottom();
77
+ if (e.detail.target.id === 'chat-log') {
78
+ scrollBottom();
79
+ // Re-run Prism highlighting after new content is swapped in
80
+ Prism.highlightAllUnder(log);
81
+ }
61
82
  });
62
83
  window.addEventListener('load', scrollBottom);
84
+ // Initial highlight on page load for any pre-existing content
85
+ document.addEventListener('DOMContentLoaded', () => {
86
+ Prism.highlightAll();
87
+ });
63
88
  })();
64
89
  </script>
65
90
  </body>
@@ -6,7 +6,7 @@
6
6
  </div>
7
7
  {% else %}
8
8
  <div class="bubble bot">
9
- {{ entry.text }}
9
+ {{ entry.text | safe }}
10
10
  <span class="chat-timestamp">{{ entry.timestamp|default(now().strftime('%H:%M')) }}{% if entry.agent %} - {{ entry.agent }}{% endif %}{% if entry.duration_ms is defined %} - {{ entry.duration_ms }}ms{% endif %}</span>
11
11
  </div>
12
12
  {% endif %}
@@ -62,4 +62,26 @@
62
62
  <button type="button" class="secondary outline" hx-get="/chat/htmx/chat-view" hx-target="#chat-content-area" hx-swap="innerHTML">Cancel</button>
63
63
  </div>
64
64
  <div id="chat-settings-saving" class="htmx-indicator" style="text-align:center; margin-top:0.5rem;"><progress indeterminate></progress></div>
65
+
66
+ {# Share Chat Link Section #}
67
+ <div style="margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid var(--pico-muted-border-color);">
68
+ <h5 style="margin-bottom: 0.5rem;">Share Chat Configuration</h5>
69
+ <p><small>Create a shareable link for this chat configuration. The current Flock and these chat settings will be frozen for the shared session.</small></p>
70
+ <a href="#"
71
+ id="shareChatHtmxLink"
72
+ hx-post="/ui/htmx/share/chat/generate-link"
73
+ hx-target="#shareChatLinkDisplayArea"
74
+ hx-swap="innerHTML"
75
+ hx-indicator="#share-chat-loading-indicator"
76
+ hx-include="closest form" {# Includes all fields from the parent form #}
77
+ style="text-decoration: underline; cursor: pointer; color: var(--pico-primary);">
78
+ Create shareable chat link...
79
+ </a>
80
+ <span id="share-chat-loading-indicator" class="htmx-indicator" style="margin-left: 0.5rem;">
81
+ <progress indeterminate style="width: 100px;"></progress>
82
+ </span>
83
+ <div id="shareChatLinkDisplayArea" style="margin-top: 1rem;">
84
+ <!-- Shareable chat link will be loaded here by HTMX -->
85
+ </div>
86
+ </div>
65
87
  </form>
@@ -36,6 +36,20 @@
36
36
  </div>
37
37
 
38
38
  <button type="submit" {% if not flock.agents %}disabled{% endif %}>Run Flock</button>
39
+
40
+ <div id="share-agent-link-container" style="margin-top: 0.5rem;">
41
+ <a href="#"
42
+ id="shareAgentHtmxLink"
43
+ hx-post="/ui/htmx/share/generate-link"
44
+ hx-target="#shareLinkDisplayArea"
45
+ hx-swap="innerHTML"
46
+ hx-indicator="#share-loading-indicator"
47
+ hx-include="#start_agent_name_select"
48
+ style="text-decoration: underline; cursor: pointer; color: var(--pico-primary);">
49
+ Create shareable link...
50
+ </a>
51
+ </div>
52
+
39
53
  <span id="run-loading-indicator" class="htmx-indicator">
40
54
  <progress indeterminate></progress> Running...
41
55
  </span>
@@ -45,4 +59,17 @@
45
59
  {% else %}
46
60
  <p>Load or create a Flock to enable execution.</p>
47
61
  {% endif %}
48
- </article>
62
+
63
+ <div id="shareLinkDisplayArea" style="margin-top: 1rem;">
64
+ <!-- Content will be loaded here by HTMX -->
65
+ </div>
66
+
67
+ </article>
68
+
69
+ <script>
70
+ document.addEventListener('DOMContentLoaded', function() {
71
+ // All previous JavaScript for toggling share link visibility is removed.
72
+ // If there are other unrelated JavaScript functions in this script block,
73
+ // they would remain.
74
+ });
75
+ </script>
@@ -0,0 +1,11 @@
1
+ {# This snippet is returned by the /ui/htmx/share/chat/generate-link endpoint #}
2
+ {% if share_url %}
3
+ <p style="margin-bottom: 0.5rem;"><strong>Shareable Chat Link generated:</strong></p>
4
+ <input type="text" id="generatedShareChatLinkInput" value="{{ share_url }}" readonly style="width: 100%; margin-bottom: 0.5rem;">
5
+ <p><small>You can select the link above and copy it (Ctrl+C or Cmd+C).</small></p>
6
+ <p><small>This link will start a chat session with the agent '{{ agent_name }}' from Flock '{{ flock_name }}' using the currently saved settings.</small></p>
7
+ {% elif error_message %}
8
+ <p style="color: var(--pico-form-invalid-color);">Error generating chat link: {{ error_message }}</p>
9
+ {% else %}
10
+ <p><em>Something went wrong, no chat link generated.</em></p>
11
+ {% endif %}
@@ -0,0 +1,35 @@
1
+ {# This snippet is returned by the /ui/htmx/share/generate-link endpoint #}
2
+ {% if share_url %}
3
+ <p style="margin-bottom: 0.5rem;"><strong>Shareable Link generated:</strong></p>
4
+ <input type="text" id="generatedShareLinkInput" value="{{ share_url }}" readonly style="width: 100%; margin-bottom: 0.5rem;">
5
+ {# <button type="button" onclick="copyGeneratedLinkToClipboard()">Copy Link</button> #}
6
+ {# <small id="copyGeneratedStatusMsg" style="margin-left: 0.5rem;"></small> #}
7
+ <p><small>You can select the link above and copy it (Ctrl+C or Cmd+C).</small></p>
8
+ {% elif error_message %}
9
+ <p style="color: var(--pico-form-invalid-color);">Error: {{ error_message }}</p>
10
+ {% else %}
11
+ <p><em>Something went wrong, no link generated.</em></p>
12
+ {% endif %}
13
+
14
+ {#
15
+ <script>
16
+ // Minimal JS for copy if desired - but user asked for JS-free.
17
+ // If you re-add the button, uncomment this.
18
+ function copyGeneratedLinkToClipboard() {
19
+ const input = document.getElementById('generatedShareLinkInput');
20
+ const msg = document.getElementById('copyGeneratedStatusMsg');
21
+ if (!input || !msg) return;
22
+
23
+ input.select();
24
+ input.setSelectionRange(0, 99999); // For mobile devices
25
+ try {
26
+ document.execCommand('copy');
27
+ msg.textContent = 'Copied!';
28
+ setTimeout(() => { msg.textContent = ''; }, 2000);
29
+ } catch (err) {
30
+ console.error('Failed to copy text: ', err);
31
+ msg.textContent = 'Failed to copy.';
32
+ }
33
+ }
34
+ </script>
35
+ #}
@@ -0,0 +1,116 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-theme="dark">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Flock Shared Agent: {{ selected_agent_name }}</title>
7
+
8
+ {# Link to Pico.css #}
9
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css" />
10
+ <link rel="stylesheet" href="/static/css/layout.css">
11
+ <link rel="stylesheet" href="/static/css/header.css">
12
+ <link rel="stylesheet" href="/static/css/sidebar.css">
13
+ <link rel="stylesheet" href="/static/css/components.css">
14
+ <link rel="stylesheet" href="/static/css/chat.css">
15
+ <!-- Font Awesome for icons -->
16
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
17
+ {# Inline generated theme CSS variables #}
18
+ {% if theme_css %}
19
+ <style>
20
+ /* Start Theme CSS */
21
+ /* stylelint-disable */
22
+ {{ theme_css | safe }}
23
+ /* stylelint-enable */
24
+ /* End Theme CSS */
25
+ </style>
26
+ {% endif %}
27
+
28
+
29
+ {# HTMX script - ensure this is loaded for the page to work #}
30
+ <script src="https://unpkg.com/htmx.org@1.9.10" integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC" crossorigin="anonymous"></script>
31
+ <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
32
+ </head>
33
+ <body>
34
+ <main class="main-content">
35
+ <header>
36
+ <hgroup>
37
+ <h2>Run {{ flock.name }} - {{ selected_agent_name }} </h2>
38
+ </hgroup>
39
+ </header>
40
+
41
+ {% if error_message %}
42
+ <article class="error-message" role="alert">
43
+ <strong>Error:</strong> {{ error_message }}
44
+ </article>
45
+ {% endif %}
46
+
47
+ {% if selected_agent_name and not error_message %} {# Only show form if no fatal error and agent is selected #}
48
+ <div style="display: flex; gap: var(--pico-spacing, 1rem);">
49
+ <div style="flex: 1;">
50
+ <article id="execution-form-content">
51
+ <form id="agent-run-form-shared"
52
+ hx-post="/ui/api/flock/htmx/run-shared"
53
+ hx-target="#results-display"
54
+ hx-swap="innerHTML"
55
+ hx-indicator="#run-loading-indicator">
56
+
57
+ {# Hidden input for the fixed agent name #}
58
+ <input type="hidden" name="start_agent_name" value="{{ selected_agent_name }}">
59
+
60
+ {# Add share_id as a hidden input to be sent with the form #}
61
+ <input type="hidden" name="share_id" value="{{ share_id }}">
62
+
63
+ {# flock_definition_str hidden input is no longer needed #}
64
+ {# {% if flock_definition_str %} #}
65
+ {# <input type="hidden" name="flock_definition_str" value="{{ flock_definition_str }}"> #}
66
+ {# {% endif %} #}
67
+
68
+ {# Dynamically generated input fields #}
69
+ {% if input_fields %}
70
+ <h4>Inputs for <code>{{ selected_agent_name }}</code>:</h4>
71
+ {% for field in input_fields %}
72
+ <label for="agent_input_{{ field.name }}">
73
+ {{ field.name }} ({{ field.type }})<br>
74
+ {% if field.description %}<small>{{ field.description }}</small>{% endif %}
75
+ </label>
76
+ {% if field.html_type == "checkbox" %}
77
+ <input type="checkbox" id="agent_input_{{ field.name }}" name="agent_input_{{ field.name }}" role="switch">
78
+ {% elif field.html_type == "textarea" %}
79
+ <textarea id="agent_input_{{ field.name }}" name="agent_input_{{ field.name }}" placeholder="{{ field.placeholder | default('Enter value') }}"></textarea>
80
+ {% else %}
81
+ <input type="{{ field.html_type }}" id="agent_input_{{ field.name }}" name="agent_input_{{ field.name }}" placeholder="Enter {{ field.type }} value">
82
+ {% endif %}
83
+ {% endfor %}
84
+ {% elif flock and selected_agent_name in flock.agents and not flock.agents[selected_agent_name].input %}
85
+ <p>Agent <code>{{ selected_agent_name }}</code> requires no inputs.</p>
86
+ {% elif not error_message %}
87
+ <p>Could not determine inputs for agent <code>{{ selected_agent_name }}</code>. The input signature might be missing or invalid.</p>
88
+ {% endif %}
89
+
90
+ <button type="submit" class="hide-on-request">Run Agent</button>
91
+ <span id="run-loading-indicator" class="htmx-indicator">
92
+ <progress indeterminate></progress> Running...
93
+ </span>
94
+ </form>
95
+ </article>
96
+ </div>
97
+ <section class="right-pane-framed" style="flex: 2; border-left: 1px solid var(--pico-muted-border-color); padding-left: 1.5rem;">
98
+ <header style=" border-bottom: 1px solid var(--pico-muted-border-color); margin-bottom: 1rem;">
99
+ <h5>Execution Results</h5>
100
+ </header>
101
+ <div id="results-display">
102
+ <p><code>Results will appear here after running the Flock.</code></p>
103
+ </div>
104
+ </section>
105
+ </div>
106
+
107
+ {% elif not error_message %}
108
+ <p>This shared link is not configured correctly. No agent specified or an issue occurred loading the configuration.</p>
109
+ {% endif %}
110
+
111
+ <footer class="main-footer">
112
+ <small>Built with FastAPI, HTMX, Pico.CSS by 🤍 white duck 🦆 - Theme: {{ active_theme_name | default('default') }}</small>
113
+ </footer>
114
+ </main>
115
+ </body>
116
+ </html>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flock-core
3
- Version: 0.4.0b48
3
+ Version: 0.4.0b49
4
4
  Summary: Declarative LLM Orchestration at Scale
5
5
  Author-email: Andre Ratzenberger <andre.ratzenberger@whiteduck.de>
6
6
  License-File: LICENSE
@@ -8,6 +8,7 @@ Classifier: License :: OSI Approved :: MIT License
8
8
  Classifier: Operating System :: OS Independent
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Requires-Python: >=3.10
11
+ Requires-Dist: aiosqlite>=0.21.0
11
12
  Requires-Dist: chromadb>=0.6.3
12
13
  Requires-Dist: cloudpickle>=3.1.1
13
14
  Requires-Dist: datasets>=3.2.0
@@ -18,7 +19,9 @@ Requires-Dist: httpx>=0.28.1
18
19
  Requires-Dist: inspect-ai>=0.3.88
19
20
  Requires-Dist: litellm==1.69.3
20
21
  Requires-Dist: loguru>=0.7.3
22
+ Requires-Dist: markdown2>=2.5.3
21
23
  Requires-Dist: matplotlib>=3.10.0
24
+ Requires-Dist: mem0ai[graph]>=0.1.100
22
25
  Requires-Dist: msgpack>=1.1.0
23
26
  Requires-Dist: notion-client>=2.3.0
24
27
  Requires-Dist: openai==1.75.0
@@ -47,7 +50,6 @@ Requires-Dist: tiktoken>=0.8.0
47
50
  Requires-Dist: toml>=0.10.2
48
51
  Requires-Dist: tqdm>=4.67.1
49
52
  Requires-Dist: uvicorn>=0.34.0
50
- Requires-Dist: zep-python>=2.0.2
51
53
  Provides-Extra: all-tools
52
54
  Requires-Dist: azure-identity>=1.23.0; extra == 'all-tools'
53
55
  Requires-Dist: azure-search-documents>=11.5.2; extra == 'all-tools'