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

Potentially problematic release.


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

Files changed (42) hide show
  1. flock/adapter/__init__.py +14 -0
  2. flock/adapter/azure_adapter.py +68 -0
  3. flock/adapter/chroma_adapter.py +73 -0
  4. flock/adapter/faiss_adapter.py +97 -0
  5. flock/adapter/pinecone_adapter.py +51 -0
  6. flock/adapter/vector_base.py +47 -0
  7. flock/config.py +1 -1
  8. flock/core/context/context.py +20 -0
  9. flock/core/flock.py +71 -91
  10. flock/core/flock_agent.py +58 -3
  11. flock/core/flock_module.py +5 -0
  12. flock/di.py +41 -0
  13. flock/modules/enterprise_memory/README.md +99 -0
  14. flock/modules/enterprise_memory/enterprise_memory_module.py +526 -0
  15. flock/modules/mem0/mem0_module.py +79 -16
  16. flock/modules/mem0_async/async_mem0_module.py +126 -0
  17. flock/modules/memory/memory_module.py +28 -8
  18. flock/modules/performance/metrics_module.py +24 -1
  19. flock/modules/zep/__init__.py +1 -0
  20. flock/modules/zep/zep_module.py +192 -0
  21. flock/webapp/app/api/execution.py +108 -73
  22. flock/webapp/app/chat.py +96 -12
  23. flock/webapp/app/config.py +1 -1
  24. flock/webapp/app/main.py +14 -12
  25. flock/webapp/app/services/sharing_models.py +38 -0
  26. flock/webapp/app/services/sharing_store.py +60 -1
  27. flock/webapp/static/css/chat.css +2 -0
  28. flock/webapp/templates/base.html +91 -1
  29. flock/webapp/templates/chat.html +64 -1
  30. flock/webapp/templates/partials/_agent_detail_form.html +3 -3
  31. flock/webapp/templates/partials/_chat_messages.html +50 -4
  32. flock/webapp/templates/partials/_flock_properties_form.html +2 -2
  33. flock/webapp/templates/partials/_results_display.html +54 -11
  34. flock/webapp/templates/partials/_structured_data_view.html +2 -2
  35. flock/webapp/templates/shared_run_page.html +27 -0
  36. {flock_core-0.4.0b49.dist-info → flock_core-0.4.1.dist-info}/METADATA +6 -4
  37. {flock_core-0.4.0b49.dist-info → flock_core-0.4.1.dist-info}/RECORD +41 -30
  38. flock/modules/mem0graph/mem0_graph_module.py +0 -63
  39. /flock/modules/{mem0graph → mem0_async}/__init__.py +0 -0
  40. {flock_core-0.4.0b49.dist-info → flock_core-0.4.1.dist-info}/WHEEL +0 -0
  41. {flock_core-0.4.0b49.dist-info → flock_core-0.4.1.dist-info}/entry_points.txt +0 -0
  42. {flock_core-0.4.0b49.dist-info → flock_core-0.4.1.dist-info}/licenses/LICENSE +0 -0
@@ -14,8 +14,10 @@
14
14
  <link rel="stylesheet" href="/static/css/chat.css">
15
15
  <!-- Font Awesome for icons -->
16
16
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
17
+ <!-- Prism.js CSS for syntax highlighting (okaidia theme) -->
18
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css" referrerpolicy="no-referrer" />
17
19
  {# Inject Theme CSS Variables #}
18
- {% if theme_css %}
20
+ {% if theme_css and theme_css.strip() %}
19
21
  <style>
20
22
  /* Start Theme CSS */
21
23
  {{ theme_css | safe }}
@@ -27,10 +29,17 @@
27
29
  integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC"
28
30
  crossorigin="anonymous"></script>
29
31
  <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
32
+ <!-- Prism.js JS (core and autoloader) -->
33
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js" referrerpolicy="no-referrer"></script>
34
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js" referrerpolicy="no-referrer"></script>
30
35
  <!-- Removed inline styles as they're in custom.css -->
31
36
  </head>
32
37
 
33
38
  <body>
39
+ <div id="global-toast-container" style="position: fixed; top: 1rem; right: 1rem; z-index: 9999; display: flex; flex-direction: column; gap: 0.5rem;">
40
+ <!-- Toasts will be appended here -->
41
+ </div>
42
+
34
43
  <header class="top-header">
35
44
  <span><strong>🐧 Flock Playground 🐤</strong></span>
36
45
  <span id="header-flock-status-container" hx-get="/ui/htmx/header-flock-status?ui_mode={{ ui_mode }}"
@@ -104,6 +113,87 @@
104
113
  function triggerEvent(eventName, detail = {}) {
105
114
  htmx.trigger(document.body, eventName, detail);
106
115
  }
116
+
117
+ // Add HTMX event listener for Prism highlighting
118
+ document.addEventListener('htmx:afterSwap', function(event) {
119
+ // Check if the swapped element or its parent is the main content area or results display
120
+ const mainContentArea = document.getElementById('main-content-area');
121
+ const resultsDisplay = document.getElementById('results-display-content');
122
+ let targetElement = event.detail.target;
123
+
124
+ if (targetElement === mainContentArea || (resultsDisplay && resultsDisplay.contains(targetElement)) || (mainContentArea && mainContentArea.contains(targetElement))) {
125
+ if (typeof Prism !== 'undefined') {
126
+ // console.log('Prism highlighting triggered for swapped content in main area.');
127
+ Prism.highlightAllUnder(mainContentArea || document.documentElement);
128
+ }
129
+ }
130
+ });
131
+
132
+ // Initial highlight on page load
133
+ document.addEventListener('DOMContentLoaded', () => {
134
+ if (typeof Prism !== 'undefined') {
135
+ // console.log('Prism initial highlighting.');
136
+ Prism.highlightAll();
137
+ }
138
+ });
139
+ </script>
140
+ {# Global Toast Handler - identical to the one in chat.html #}
141
+ <script>
142
+ document.body.addEventListener('showGlobalToast', function (event) {
143
+ const detail = event.detail;
144
+ const message = detail.message || 'Done!';
145
+ const type = detail.type || 'info'; // success, error, warning, info
146
+
147
+ const toastContainer = document.getElementById('global-toast-container');
148
+ if (!toastContainer) return;
149
+
150
+ const toast = document.createElement('article');
151
+ toast.className = `toast-message ${type}`;
152
+ toast.innerHTML = `<button class="close-toast" style="float: right; background: none; border: none; font-size: 1.2rem; line-height: 1; padding: 0.25rem 0.5rem; cursor: pointer;">&times;</button>${message}`;
153
+
154
+ toast.style.padding = '0.75rem';
155
+ toast.style.border = '1px solid';
156
+ toast.style.borderRadius = 'var(--pico-border-radius, 4px)';
157
+ toast.style.minWidth = '250px';
158
+ toast.style.maxWidth = '400px';
159
+ toast.style.opacity = '0';
160
+ toast.style.transition = 'opacity 0.3s ease-in-out, transform 0.3s ease-in-out';
161
+ toast.style.transform = 'translateX(100%)';
162
+
163
+ if (type === 'success') {
164
+ toast.style.borderColor = 'var(--pico-color-green-500, green)';
165
+ toast.style.backgroundColor = 'var(--pico-color-green-150, #e6fffa)';
166
+ toast.style.color = 'var(--pico-color-green-700, darkgreen)';
167
+ } else if (type === 'error') {
168
+ toast.style.borderColor = 'var(--pico-color-red-500, red)';
169
+ toast.style.backgroundColor = 'var(--pico-color-red-150, #ffe6e6)';
170
+ toast.style.color = 'var(--pico-color-red-700, darkred)';
171
+ } else { // Default/info/warning
172
+ toast.style.borderColor = 'var(--pico-color-blue-500, blue)';
173
+ toast.style.backgroundColor = 'var(--pico-color-blue-150, #e6f7ff)';
174
+ toast.style.color = 'var(--pico-color-blue-700, darkblue)';
175
+ }
176
+
177
+ toastContainer.appendChild(toast);
178
+
179
+ setTimeout(() => {
180
+ toast.style.opacity = '1';
181
+ toast.style.transform = 'translateX(0)';
182
+ }, 10);
183
+
184
+ const closeButton = toast.querySelector('.close-toast');
185
+ closeButton.onclick = () => {
186
+ toast.style.opacity = '0';
187
+ toast.style.transform = 'translateY(-20px)';
188
+ setTimeout(() => toast.remove(), 300);
189
+ };
190
+
191
+ setTimeout(() => {
192
+ toast.style.opacity = '0';
193
+ toast.style.transform = 'translateY(-20px)';
194
+ setTimeout(() => toast.remove(), 300);
195
+ }, 5000);
196
+ });
107
197
  </script>
108
198
  </body>
109
199
 
@@ -9,7 +9,7 @@
9
9
  {# Prism.js CSS for syntax highlighting (okaidia theme) #}
10
10
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css" referrerpolicy="no-referrer" />
11
11
  {# Inject active theme variables #}
12
- {% if theme_css %}
12
+ {% if theme_css and theme_css.strip() %}
13
13
  <style>
14
14
  /* Start Theme CSS */
15
15
  /* stylelint-disable */
@@ -24,6 +24,10 @@
24
24
  <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js" referrerpolicy="no-referrer"></script>
25
25
  </head>
26
26
  <body class="chat-page">
27
+ <div id="global-toast-container" style="position: fixed; top: 1rem; right: 1rem; z-index: 9999; display: flex; flex-direction: column; gap: 0.5rem;">
28
+ <!-- Toasts will be appended here -->
29
+ </div>
30
+
27
31
  <div id="chat-container">
28
32
  <div class="chat-header" style="justify-content: space-between;">
29
33
  <hgroup>
@@ -86,6 +90,65 @@
86
90
  Prism.highlightAll();
87
91
  });
88
92
  })();
93
+
94
+ // Global Toast Handler
95
+ document.body.addEventListener('showGlobalToast', function (event) {
96
+ const detail = event.detail;
97
+ const message = detail.message || 'Done!';
98
+ const type = detail.type || 'info'; // success, error, warning, info
99
+
100
+ const toastContainer = document.getElementById('global-toast-container');
101
+ if (!toastContainer) return;
102
+
103
+ const toast = document.createElement('article');
104
+ toast.className = `toast-message ${type}`;
105
+ toast.innerHTML = `<button class="close-toast" style="float: right; background: none; border: none; font-size: 1.2rem; line-height: 1; padding: 0.25rem 0.5rem; cursor: pointer;">&times;</button>${message}`;
106
+
107
+ // Basic styling, can be enhanced in CSS file
108
+ toast.style.padding = '0.75rem';
109
+ toast.style.border = '1px solid';
110
+ toast.style.borderRadius = 'var(--pico-border-radius, 4px)';
111
+ toast.style.minWidth = '250px';
112
+ toast.style.maxWidth = '400px';
113
+ toast.style.opacity = '0'; // Start faded out
114
+ toast.style.transition = 'opacity 0.3s ease-in-out, transform 0.3s ease-in-out';
115
+ toast.style.transform = 'translateX(100%)'; // Start off-screen
116
+
117
+ if (type === 'success') {
118
+ toast.style.borderColor = 'var(--pico-color-green-500, green)';
119
+ toast.style.backgroundColor = 'var(--pico-color-green-150, #e6fffa)';
120
+ toast.style.color = 'var(--pico-color-green-700, darkgreen)';
121
+ } else if (type === 'error') {
122
+ toast.style.borderColor = 'var(--pico-color-red-500, red)';
123
+ toast.style.backgroundColor = 'var(--pico-color-red-150, #ffe6e6)';
124
+ toast.style.color = 'var(--pico-color-red-700, darkred)';
125
+ } else { // Default/info/warning
126
+ toast.style.borderColor = 'var(--pico-color-blue-500, blue)';
127
+ toast.style.backgroundColor = 'var(--pico-color-blue-150, #e6f7ff)';
128
+ toast.style.color = 'var(--pico-color-blue-700, darkblue)';
129
+ }
130
+
131
+ toastContainer.appendChild(toast);
132
+
133
+ // Animate in
134
+ setTimeout(() => {
135
+ toast.style.opacity = '1';
136
+ toast.style.transform = 'translateX(0)';
137
+ }, 10); // Small delay to allow CSS to apply initial state
138
+
139
+ const closeButton = toast.querySelector('.close-toast');
140
+ closeButton.onclick = () => {
141
+ toast.style.opacity = '0';
142
+ toast.style.transform = 'translateY(-20px)';
143
+ setTimeout(() => toast.remove(), 300);
144
+ };
145
+
146
+ setTimeout(() => {
147
+ toast.style.opacity = '0';
148
+ toast.style.transform = 'translateY(-20px)';
149
+ setTimeout(() => toast.remove(), 300);
150
+ }, 5000); // Auto-dismiss after 5 seconds
151
+ });
89
152
  </script>
90
153
  </body>
91
154
  </html>
@@ -14,9 +14,9 @@
14
14
  {% endif %}
15
15
 
16
16
  <form {% if is_new %}
17
- hx-post="/api/flock/htmx/agents"
17
+ hx-post="/ui/api/flock/htmx/agents"
18
18
  {% else %}
19
- hx-put="/api/flock/htmx/agents/{{ agent.name if agent else '' }}"
19
+ hx-put="/ui/api/flock/htmx/agents/{{ agent.name if agent else '' }}"
20
20
  {% endif %}
21
21
  hx-target="#agent-detail-form-content"
22
22
  hx-swap="innerHTML"
@@ -77,7 +77,7 @@
77
77
  </button>
78
78
  {% if not is_new and agent %}
79
79
  <button type="button" role="button" class="secondary outline"
80
- hx-delete="/api/flock/htmx/agents/{{ agent.name }}"
80
+ hx-delete="/ui/api/flock/htmx/agents/{{ agent.name }}"
81
81
  hx-target="#agent-detail-form-content"
82
82
  hx-confirm="Are you sure you want to delete agent '{{ agent.name }}'?"
83
83
  hx-indicator="#agent-detail-loading-indicator">
@@ -5,9 +5,55 @@
5
5
  <span class="chat-timestamp">{{ entry.timestamp|default(now().strftime('%H:%M')) }}</span>
6
6
  </div>
7
7
  {% else %}
8
- <div class="bubble bot">
9
- {{ entry.text | safe }}
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>
8
+ <div class="bubble bot" x-data="{showForm:false}">
9
+ <!-- Bubble content (visible when feedback form hidden) -->
10
+ <div x-show="!showForm">
11
+ <div>
12
+ {{ entry.text | safe }}
13
+ <span class="chat-timestamp">
14
+ {{ entry.timestamp|default(now().strftime('%H:%M')) }}{% if entry.agent %} - {{ entry.agent }}{% endif %}{% if entry.duration_ms is defined %} - {{ entry.duration_ms }}ms - {% endif %}
15
+ <!-- hidden meta form (used by thumbs links) -->
16
+ <form class="feedback-meta" style="display:none">
17
+ {% if share_id %}<input type="hidden" name="share_id" value="{{ share_id }}">{% endif %}
18
+ <input type="hidden" name="flock_definition" value="{{ entry.flock_yaml | replace('"', '&quot;') }}">
19
+ <input type="hidden" name="agent_name" value="{{ entry.agent }}">
20
+ <input type="hidden" name="actual_response" value='{{ entry.raw_json | replace("'", "&#39;") }}'>
21
+ <input type="hidden" name="reason" value="positive">
22
+ </form>
23
+
24
+ <a href="#"
25
+ hx-post="{% if share_id %}/chat/htmx/feedback-shared{% else %}/chat/htmx/feedback{% endif %}"
26
+ hx-include="closest .feedback-meta"
27
+ hx-target="closest .bubble"
28
+ hx-swap="innerHTML"
29
+ title="Looks good!" class="feedback-link">👍</a>
30
+ -
31
+ <a href="#" @click.prevent="showForm=true" title="Submit detailed feedback" class="feedback-link">👎</a>
32
+ </span>
33
+ </div>
34
+ </div>
35
+
36
+ <!-- Feedback form (initially hidden, toggled by 👎 link) -->
37
+ <div x-show="showForm" style="width:800px;">
38
+ <form
39
+ hx-post="{% if share_id %}/chat/htmx/feedback-shared{% else %}/chat/htmx/feedback{% endif %}"
40
+ hx-swap="outerHTML"
41
+ x-on:htmx:afterRequest="showForm=false"
42
+ style="display:block; width:800px;">
43
+ {% if share_id %}<input type="hidden" name="share_id" value="{{ share_id }}">{% endif %}
44
+ <input type="hidden" name="flock_definition" value='{{ entry.flock_yaml | tojson | safe }}'>
45
+ <input type="hidden" name="agent_name" value='{{ entry.agent | tojson | safe }}'>
46
+ <input type="hidden" name="actual_response" value='{{ entry.raw_json | tojson | safe }}'>
47
+ <label>Reason</label>
48
+ <textarea name="reason" required style="min-height:5rem;width:100%; white-space:pre-wrap;"></textarea>
49
+ <label>Expected / Correct Response</label>
50
+ <textarea name="expected_response" style="min-height:12rem;width:100%; white-space:pre-wrap;font-family:monospace;">{{ entry.raw_json }}</textarea>
51
+ <div style="margin-top:0.4rem; display:flex; gap:0.5rem;">
52
+ <button type="submit" class="secondary">Send</button>
53
+ <button type="button" class="outline" @click="showForm=false">Back...</button>
54
+ </div>
55
+ </form>
56
+ </div>
11
57
  </div>
12
58
  {% endif %}
13
- {% endfor %}
59
+ {% endfor %}
@@ -23,7 +23,7 @@
23
23
  </div>
24
24
  {% endif %}
25
25
 
26
- <form hx-post="/api/flock/htmx/flock-properties" hx-target="#flock-properties-form-article" hx-swap="innerHTML" hx-indicator="#flock-props-loading">
26
+ <form hx-post="/ui/api/flock/htmx/flock-properties" hx-target="#flock-properties-form-article" hx-swap="innerHTML" hx-indicator="#flock-props-loading">
27
27
  <label for="flock_name">Flock Name</label>
28
28
  <input type="text" id="flock_name" name="flock_name" value="{{ flock.name if flock else '' }}" required>
29
29
 
@@ -38,7 +38,7 @@
38
38
  </div>
39
39
  </form>
40
40
  <hr>
41
- <form hx-post="/api/flock/htmx/save-flock" hx-target="#flock-properties-form-article" hx-swap="innerHTML" hx-indicator="#flock-save-loading">
41
+ <form hx-post="/ui/api/flock/htmx/save-flock" hx-target="#flock-properties-form-article" hx-swap="innerHTML" hx-indicator="#flock-save-loading">
42
42
  <label for="save_filename">Save Flock As:</label>
43
43
  <input type="text" id="save_filename" name="save_filename"
44
44
  value="{{ current_filename if current_filename else (flock.name.replace(' ', '_').lower() + '.flock.yaml' if flock and flock.name else 'my_flock.flock.yaml') }}"
@@ -6,30 +6,73 @@
6
6
  <div style="text-align: right;">
7
7
  <button role="button" class="outline contrast" @click="viewMode = 'structured'" :aria-pressed="viewMode === 'structured'">Structured</button>
8
8
  <button role="button" class="outline contrast" @click="viewMode = 'json'" :aria-pressed="viewMode === 'json'">Raw JSON</button>
9
+
10
+ <div style="display:inline-block;margin-left:0.5rem;">
11
+ <!-- Quick thumbs-up feedback (inline form so we have the identifiers) -->
12
+ <form hx-post="{{ feedback_endpoint }}" hx-target="#results-display-content" hx-swap="innerHTML" style="display:inline-block;vertical-align:middle;margin:0;" hx-indicator="#feedback-loading-indicator">
13
+ {% if share_id %}<input type="hidden" name="share_id" value="{{ share_id }}">{% endif %}
14
+ <input type="hidden" name="flock_name" value="{{ flock_name }}">
15
+ <input type="hidden" name="agent_name" value="{{ agent_name }}">
16
+ <input type="hidden" name="flock_definition" value='{{ flock_definition | replace("'", "&#39;") }}'>
17
+ <input type="hidden" name="actual_response" value='{{ result_raw_json | replace("'", "&#39;") }}'>
18
+ <input type="hidden" name="reason" value="positive">
19
+ <button type="submit" role="button" class="outline contrast" title="Looks good!" style="padding:0.3rem 0.5rem;line-height:1;vertical-align:middle;margin:0;"><i class="fa fa-thumbs-up"></i></button>
20
+ </form>
21
+
22
+ <!-- Thumbs-down toggles detailed feedback form -->
23
+ <button role="button" class="outline contrast" @click="viewMode = 'feedback'" :aria-pressed="viewMode === 'feedback'" title="Something's wrong" style="padding:0.3rem 0.5rem;line-height:1;vertical-align:middle;margin:0;"><i class="fa fa-thumbs-down"></i></button>
24
+ </div>
9
25
  </div>
10
26
  </header>
11
27
 
12
28
  <div x-show="viewMode === 'json'">
13
- {% if result_data is string %}
14
- <p class="error" style="white-space: pre-wrap;">{{ result_data }}</p>
15
- {% elif result_data %}
16
- <pre><code style="word-break: break-all; white-space: pre-wrap;">{{ result_data | tojson(indent=2) }}</code></pre>
29
+ {# Check for the new result_raw_json variable first #}
30
+ {% if result_raw_json is defined %}
31
+ <pre><code class="language-json" style="word-break: break-all; white-space: pre-wrap;">{{ result_raw_json }}</code></pre>
32
+ {# Fallback for old context or if result was an error string from backend before formatting #}
33
+ {% elif result is string %}
34
+ <pre><code class="language-plaintext" style="word-break: break-all; white-space: pre-wrap;">{{ result }}</code></pre>
35
+ {% elif result %}
36
+ {# Fallback if result_raw_json is somehow not provided but result (dict) is #}
37
+ <pre><code class="language-json" style="word-break: break-all; white-space: pre-wrap;">{{ result | tojson(indent=2) }}</code></pre>
17
38
  {% else %}
18
39
  <p>No results to display yet.</p>
19
40
  {% endif %}
20
41
  </div>
21
42
 
22
43
  <div x-show="viewMode === 'structured'">
23
- {% if result_data is string %}
24
- <p class="error" style="white-space: pre-wrap;">{{ result_data }}</p>
25
- {% elif result_data is mapping %}
26
- {# Call our new macro for structured display #}
27
- {{ render_value(result_data) }}
28
- {% elif result_data %}
44
+ {% if result is string %}
45
+ <p class="error" style="white-space: pre-wrap;">{{ result }}</p>
46
+ {% elif result is mapping %}
47
+ {{ render_value(result) }}
48
+ {% elif result %}
29
49
  <p>Structured view not available for this result type (not a dictionary).</p>
30
- <pre><code>{{ result_data | tojson(indent=2) }}</code></pre>
50
+ <pre><code class="language-json" style="word-break: break-all; white-space: pre-wrap;">{{ result | tojson(indent=2) }}</code></pre>
31
51
  {% else %}
32
52
  <p>No results to display yet.</p>
33
53
  {% endif %}
34
54
  </div>
55
+
56
+ <!-- ---------------- Feedback form ---------------- -->
57
+ <div x-show="viewMode === 'feedback'" style="margin-top: 1rem;">
58
+ <form hx-post="{{ feedback_endpoint }}" hx-target="#results-display-content" hx-swap="innerHTML" hx-indicator="#feedback-loading-indicator">
59
+ {# Hidden identifiers #}
60
+ {% if share_id %}
61
+ <input type="hidden" name="share_id" value="{{ share_id }}">
62
+ {% endif %}
63
+ <input type="hidden" name="flock_name" value="{{ flock_name }}">
64
+ <input type="hidden" name="agent_name" value="{{ agent_name }}">
65
+ <input type="hidden" name="flock_definition" value='{{ flock_definition | replace("'", "&#39;") }}'>
66
+ <input type="hidden" name="actual_response" value='{{ result_raw_json | replace("'", "&#39;") }}'>
67
+
68
+ <label for="feedback_reason">Reason / Comment</label>
69
+ <textarea id="feedback_reason" name="reason" placeholder="Why isn't the answer good?" required style="min-height:6rem;"></textarea>
70
+
71
+ <label for="expected_response" style="margin-top:0.5rem;">Correct / Expected Response (JSON or text)</label>
72
+ <textarea id="expected_response" name="expected_response" style="white-space: pre; font-family: monospace; min-height:10rem;">{{ result_raw_json }}</textarea>
73
+
74
+ <button type="submit" class="secondary" style="margin-top:0.5rem;">Send Feedback</button>
75
+ <span id="feedback-loading-indicator" class="htmx-indicator"><progress indeterminate></progress> Sending…</span>
76
+ </form>
77
+ </div>
35
78
  </article>
@@ -31,8 +31,8 @@
31
31
  {% elif value is none %}
32
32
  <em style="color: var(--pico-code-color);">None</em>
33
33
  {% else %}
34
- {# Apply pre-wrap for multi-line strings #}
35
- <span style="white-space: pre-wrap; word-break: break-word;">{{ value }}</span>
34
+ {# Apply pre-wrap for multi-line strings and render markdown #}
35
+ <div class="markdown-content" style="word-break: break-word;">{{ value | markdown | safe }}</div>
36
36
  {% endif %}
37
37
  {% endmacro %}
38
38
 
@@ -14,6 +14,8 @@
14
14
  <link rel="stylesheet" href="/static/css/chat.css">
15
15
  <!-- Font Awesome for icons -->
16
16
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
17
+ <!-- Prism.js CSS for syntax highlighting (okaidia theme) -->
18
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css" referrerpolicy="no-referrer" />
17
19
  {# Inline generated theme CSS variables #}
18
20
  {% if theme_css %}
19
21
  <style>
@@ -112,5 +114,30 @@
112
114
  <small>Built with FastAPI, HTMX, Pico.CSS by 🤍 white duck 🦆 - Theme: {{ active_theme_name | default('default') }}</small>
113
115
  </footer>
114
116
  </main>
117
+
118
+ <!-- Prism.js JS (core and autoloader) -->
119
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js" referrerpolicy="no-referrer"></script>
120
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js" referrerpolicy="no-referrer"></script>
121
+ <script>
122
+ // Add HTMX event listener for Prism highlighting
123
+ document.addEventListener('htmx:afterSwap', function(event) {
124
+ const resultsDisplay = document.getElementById('results-display');
125
+ // Check if the swapped element is the results display or a child of it
126
+ if (resultsDisplay && (event.detail.target === resultsDisplay || resultsDisplay.contains(event.detail.target))) {
127
+ if (typeof Prism !== 'undefined') {
128
+ // console.log('Prism highlighting triggered for swapped content in results-display.');
129
+ Prism.highlightAllUnder(resultsDisplay);
130
+ }
131
+ }
132
+ });
133
+
134
+ // Initial highlight on page load
135
+ document.addEventListener('DOMContentLoaded', () => {
136
+ if (typeof Prism !== 'undefined') {
137
+ // console.log('Prism initial highlighting on shared page.');
138
+ Prism.highlightAll();
139
+ }
140
+ });
141
+ </script>
115
142
  </body>
116
143
  </html>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flock-core
3
- Version: 0.4.0b49
3
+ Version: 0.4.1
4
4
  Summary: Declarative LLM Orchestration at Scale
5
5
  Author-email: Andre Ratzenberger <andre.ratzenberger@whiteduck.de>
6
6
  License-File: LICENSE
@@ -21,7 +21,7 @@ Requires-Dist: litellm==1.69.3
21
21
  Requires-Dist: loguru>=0.7.3
22
22
  Requires-Dist: markdown2>=2.5.3
23
23
  Requires-Dist: matplotlib>=3.10.0
24
- Requires-Dist: mem0ai[graph]>=0.1.100
24
+ Requires-Dist: mem0ai[graph]>=0.1.101
25
25
  Requires-Dist: msgpack>=1.1.0
26
26
  Requires-Dist: notion-client>=2.3.0
27
27
  Requires-Dist: openai==1.75.0
@@ -50,6 +50,8 @@ Requires-Dist: tiktoken>=0.8.0
50
50
  Requires-Dist: toml>=0.10.2
51
51
  Requires-Dist: tqdm>=4.67.1
52
52
  Requires-Dist: uvicorn>=0.34.0
53
+ Requires-Dist: wd-di>=0.2.14
54
+ Requires-Dist: zep-python>=2.0.2
53
55
  Provides-Extra: all-tools
54
56
  Requires-Dist: azure-identity>=1.23.0; extra == 'all-tools'
55
57
  Requires-Dist: azure-search-documents>=11.5.2; extra == 'all-tools'
@@ -91,7 +93,7 @@ Description-Content-Type: text/markdown
91
93
 
92
94
  🐤 `pip install flock-core` will install the latest non-beta version 🐤
93
95
 
94
- 🐤 Expected Release for 0.4.0 `Magpie`: End of April 2025 🐤
96
+ 🐤 The 0.4.0 Magpie release is in the works! 🐤
95
97
 
96
98
  ---
97
99
 
@@ -296,7 +298,7 @@ Flock makes this easy with:
296
298
  * **Declarative Configuration:** Define Temporal timeouts, retry policies, and task queues directly within your `Flock` and `FlockAgent` configurations (YAML or Python).
297
299
  * **Correct Patterns:** Uses Temporal's recommended granular activity execution for better control and visibility.
298
300
  * **Clear Worker Separation:** Provides guidance and flags for running dedicated Temporal workers, separating development convenience from production best practices.
299
-
301
+
300
302
  Visit the [Temporal Documentation](https://learn.temporal.io/python/workflows/) for more information on how to use Temporal.
301
303
 
302
304
  Or check out the [Flock Showcase](https://github.com/whiteducksoftware/flock-showcase) for a complete example of a Flock that uses Temporal or our [docs](https://whiteducksoftware.github.io/flock/guides/temporal-configuration/) for more information.