zaturn 0.2.2__py3-none-any.whl → 0.3.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.
Files changed (40) hide show
  1. zaturn/studio/agent_wrapper.py +36 -112
  2. zaturn/studio/app.py +48 -41
  3. zaturn/studio/storage.py +14 -4
  4. zaturn/studio/templates/_shell.html +3 -3
  5. zaturn/studio/templates/ai_message.html +1 -2
  6. zaturn/studio/templates/c_source_card.html +3 -3
  7. zaturn/studio/templates/chat.html +27 -19
  8. zaturn/studio/templates/chat_metadata.html +10 -0
  9. zaturn/studio/templates/css/style.css +146 -21
  10. zaturn/studio/templates/function_call.html +5 -4
  11. zaturn/studio/templates/icons/arrow-left.svg +3 -0
  12. zaturn/studio/templates/icons/arrow-right.svg +3 -0
  13. zaturn/studio/templates/icons/chat-bubble.svg +6 -0
  14. zaturn/studio/templates/icons/check-circle-solid.svg +3 -0
  15. zaturn/studio/templates/icons/database.svg +5 -0
  16. zaturn/studio/templates/icons/fire-flame.svg +4 -0
  17. zaturn/studio/templates/icons/floppy-disk.svg +5 -0
  18. zaturn/studio/templates/icons/link.svg +4 -0
  19. zaturn/studio/templates/icons/play.svg +3 -0
  20. zaturn/studio/templates/icons/settings.svg +4 -0
  21. zaturn/studio/templates/icons/timer.svg +5 -0
  22. zaturn/studio/templates/icons/trash.svg +4 -0
  23. zaturn/studio/templates/icons/upload.svg +4 -0
  24. zaturn/studio/templates/icons/user.svg +4 -0
  25. zaturn/studio/templates/icons/warning-triangle.svg +5 -0
  26. zaturn/studio/templates/icons/wrench.svg +4 -0
  27. zaturn/studio/templates/loader.html +1 -1
  28. zaturn/studio/templates/manage_sources.html +5 -5
  29. zaturn/studio/templates/new_conversation.html +2 -2
  30. zaturn/studio/templates/settings.html +24 -3
  31. zaturn/studio/templates/setup_prompt.html +3 -2
  32. zaturn/studio/templates/user_message.html +2 -2
  33. {zaturn-0.2.2.dist-info → zaturn-0.3.0.dist-info}/METADATA +4 -3
  34. zaturn-0.3.0.dist-info/RECORD +55 -0
  35. zaturn/studio/static/noto_emoji.ttf +0 -0
  36. zaturn-0.2.2.dist-info/RECORD +0 -39
  37. {zaturn-0.2.2.dist-info → zaturn-0.3.0.dist-info}/WHEEL +0 -0
  38. {zaturn-0.2.2.dist-info → zaturn-0.3.0.dist-info}/entry_points.txt +0 -0
  39. {zaturn-0.2.2.dist-info → zaturn-0.3.0.dist-info}/licenses/LICENSE +0 -0
  40. {zaturn-0.2.2.dist-info → zaturn-0.3.0.dist-info}/top_level.txt +0 -0
@@ -4,128 +4,52 @@ import json
4
4
  from function_schema import get_function_schema
5
5
  import httpx
6
6
  from mcp.types import ImageContent
7
+ from pydantic_ai import Agent
8
+ from pydantic_ai.messages import ModelMessagesTypeAdapter
9
+ from pydantic_ai.models.openai import OpenAIChatModel, OpenAIChatModelSettings
10
+ from pydantic_ai.providers.openai import OpenAIProvider
11
+ from pydantic_core import to_jsonable_python
7
12
 
8
13
 
9
- class Agent:
14
+ class ZaturnAgent:
10
15
 
11
16
  def __init__(self,
12
17
  endpoint: str,
13
18
  api_key: str,
14
- model: str,
19
+ model_name: str,
15
20
  tools: list = [],
16
21
  image_input: bool = False,
22
+ reasoning_effort: str = 'none',
23
+ system_prompt: str = '',
17
24
  ):
18
25
 
19
- self._post_url = f'{endpoint}/chat/completions'
20
- self._api_key = api_key
21
- self._model = model
22
- self._image_input = image_input
23
- self._system_message = {
24
- 'role': 'system',
25
- 'content': """
26
- You are a helpful data analysis assistant.
27
- Use only the tool provided data sources to process user inputs.
28
- Do not use external sources or your own knowledge base.
29
- Also, the tool outputs are shown to the user.
30
- So, please avoid repeating the tool outputs in the generated text.
31
- Use list_sources and describe_table whenever needed,
32
- do not prompt the user for source names and column names.
33
- """,
34
- }
35
-
36
- self._tools = []
37
- self._tool_map = {}
38
- for tool in tools:
39
- tool_schema = get_function_schema(tool)
40
- self._tools.append({
41
- 'type': 'function',
42
- 'function': tool_schema,
43
- })
44
- self._tool_map[tool_schema['name']] = tool
45
-
46
-
47
- def _prepare_input_messages(self, messages):
48
- input_messages = [self._system_message]
49
- for message in messages:
50
- if message['role']!='tool':
51
- input_messages.append(message)
52
- elif type(message['content']) is not list:
53
- input_messages.append(message)
54
- else:
55
- new_content = []
56
- image_content = None
57
- for content in message['content']:
58
- if content['type']=='image_url':
59
- image_content = content
60
- new_content.append({
61
- 'type': 'text',
62
- 'text': 'Tool call returned an image to the user.',
63
- })
64
- else:
65
- new_content.append(content)
66
- input_messages.append({
67
- 'role': message['role'],
68
- 'tool_call_id': message['tool_call_id'],
69
- 'name': message['name'],
70
- 'content': new_content,
71
- })
72
-
73
- return input_messages
74
-
75
-
76
- def run(self, messages):
77
- if type(messages) is str:
78
- messages = [{'role': 'user', 'content': messages}]
79
-
80
- while True:
81
- res = httpx.post(
82
- url = self._post_url,
83
- headers = {
84
- 'Authorization': f'Bearer {self._api_key}'
85
- },
86
- json = {
87
- 'model': self._model,
88
- 'messages': self._prepare_input_messages(messages),
89
- 'tools': self._tools,
90
- 'reasoning': {'exclude': True},
91
- }
26
+ model_settings = None
27
+ reasoning_effort = reasoning_effort.lower()
28
+ if reasoning_effort in ['low', 'medium', 'high']:
29
+ model_settings = OpenAIChatModelSettings(
30
+ openai_reasoning_effort = reasoning_effort,
92
31
  )
32
+
33
+ self._agent = Agent(
34
+ OpenAIChatModel(
35
+ model_name,
36
+ provider = OpenAIProvider(
37
+ api_key = api_key,
38
+ base_url = endpoint,
39
+ )
40
+ ),
41
+ model_settings = model_settings,
42
+ system_prompt = system_prompt or None,
43
+ tools = tools,
44
+ )
45
+
93
46
 
94
- print(res.text)
95
- resj = res.json()
96
- reply = resj['choices'][0]['message']
97
- messages.append(reply)
98
-
99
- tool_calls = reply.get('tool_calls')
100
- if tool_calls:
101
- for tool_call in tool_calls:
102
- tool_name = tool_call['function']['name']
103
- tool_args = json.loads(tool_call['function']['arguments'])
104
- tool_response = self._tool_map[tool_name](**tool_args)
105
- if type(tool_response) is ImageContent:
106
- b64_data = tool_response.data
107
- data_url = f'data:image/png;base64,{b64_data}'
108
- content = [{
109
- 'type': 'image_url',
110
- 'image_url': {
111
- "url": data_url,
112
- }
113
- }]
114
- else:
115
- content = [{
116
- 'type': 'text',
117
- 'text': json.dumps(tool_response)
118
- }]
119
-
120
- messages.append({
121
- 'role': 'tool',
122
- 'tool_call_id': tool_call['id'],
123
- 'name': tool_name,
124
- 'content': content
125
- })
126
- else:
127
- break
128
-
129
- return messages
130
-
47
+ def run(self, prompt, message_history = None):
48
+ if message_history:
49
+ message_history_obj = ModelMessagesTypeAdapter.validate_python(message_history)
50
+ result = self._agent.run_sync(prompt, message_history = message_history_obj)
51
+ return to_jsonable_python(result.all_messages())
52
+ else:
53
+ result = self._agent.run_sync(prompt)
54
+ return to_jsonable_python(result.all_messages())
131
55
 
zaturn/studio/app.py CHANGED
@@ -62,6 +62,8 @@ def save_settings() -> str:
62
62
  app.config['state']['api_model'] = api_model
63
63
  app.config['state']['api_endpoint'] = api_endpoint
64
64
  app.config['state']['api_image_input'] = False
65
+ app.config['state']['reasoning_effort'] = request.form.get('reasoning_effort', 'none')
66
+ app.config['state']['system_prompt'] = request.form.get('system_prompt').strip('\n')
65
67
 
66
68
  try:
67
69
  model_info = httpx.get(
@@ -195,31 +197,34 @@ def get_active_sources():
195
197
 
196
198
  def prepare_chat_for_render(chat):
197
199
  fn_calls = {}
198
- for msg in chat['messages']:
199
- if msg.get('role')=='assistant':
200
- if msg.get('tool_calls'):
201
- msg['is_tool_call'] = True
202
- for tool_call in msg['tool_calls']:
203
- fn_call = tool_call['function']
204
- fn_call['arguments'] = tomli_w.dumps(
205
- json.loads(fn_call['arguments'])
206
- ).replace('\n', '<br>')
207
- fn_calls[tool_call['id']] = fn_call
208
- else:
209
- msg['html'] = mistune.html(msg['content'])
210
- if msg.get('role')=='tool':
211
- msg['call_details'] = fn_calls[msg['tool_call_id']]
212
- if type(msg['content']) is str:
213
- msg['html'] = mistune.html(json.loads(msg['text']))
214
- elif type(msg['content']) is list:
215
- msg['html'] = ''
216
- for content in msg['content']:
217
- if content['type'] == 'image_url':
218
- data_url = content['image_url']['url']
219
- msg['html'] += f'<img src="{data_url}">'
220
- else:
221
- msg['html'] += mistune.html(json.loads(content['text']))
222
-
200
+
201
+ for message in chat['messages']:
202
+ for part in message['parts']:
203
+ if part['part_kind']=='text' and message['kind']=='response':
204
+ part['html_content'] = mistune.html(part['content'])
205
+ elif part['part_kind']=='tool-call':
206
+ fn_calls[part['tool_call_id']] = part
207
+ fn_calls[part['tool_call_id']]['timestamp'] = message['timestamp']
208
+ elif part['part_kind']=='tool-return':
209
+ fn_call = fn_calls[part['tool_call_id']]
210
+ part['call_details'] = {}
211
+ part['call_details']['name'] = fn_call['tool_name']
212
+
213
+ t1 = datetime.fromisoformat(fn_call['timestamp'])
214
+ t2 = datetime.fromisoformat(part['timestamp'])
215
+ part['call_details']['exec_time'] = (t2 - t1).seconds
216
+
217
+ part['call_details']['args_html'] = tomli_w.dumps(
218
+ json.loads(fn_call['args'])
219
+ ).replace('\n', '<br>')
220
+
221
+ if type(part['content']) is str:
222
+ part['html_content'] = mistune.html(part['content'])
223
+ elif type(part['content']) is dict and part['content']['type']=='image':
224
+ data_url = f"data:{part['content']['mimeType']};base64,{part['content']['data']}"
225
+ part['html_content'] = f'<img src="{data_url}">'
226
+
227
+
223
228
  return chat
224
229
 
225
230
 
@@ -230,22 +235,23 @@ def create_new_chat():
230
235
  chat = storage.load_chat(slug)
231
236
 
232
237
  state = app.config['state']
233
- agent = agent_wrapper.Agent(
238
+ agent = agent_wrapper.ZaturnAgent(
234
239
  endpoint = state['api_endpoint'],
235
240
  api_key = state['api_key'],
236
- model = state['api_model'],
241
+ model_name = state['api_model'],
237
242
  tools = ZaturnTools(get_active_sources()).tools,
238
243
  image_input = state['api_image_input'],
244
+ reasoning_effort = state['reasoning_effort'],
245
+ system_prompt = state['system_prompt'],
239
246
  )
240
- chat['messages'] = agent.run(chat['messages'])
247
+ chat['messages'] = agent.run(question)
241
248
  storage.save_chat(slug, chat)
242
- chat = prepare_chat_for_render(chat)
243
249
 
244
250
  return boost(
245
251
  ''.join([
246
252
  render_template('nav.html', slugs=storage.list_chats()),
247
253
  '<main id="main">',
248
- render_template('chat.html', chat=chat),
254
+ render_template('chat.html', chat=prepare_chat_for_render(chat)),
249
255
  '</main>'
250
256
  ]),
251
257
  reswap = 'multi:#sidebar,#main',
@@ -264,25 +270,26 @@ def show_chat(slug: str):
264
270
  def follow_up_message():
265
271
  slug = request.form['slug']
266
272
  chat = storage.load_chat(slug)
267
- chat['messages'].append({
268
- 'role': 'user',
269
- 'content': request.form['question'],
270
- })
271
-
273
+
272
274
  state = app.config['state']
273
- agent = agent_wrapper.Agent(
275
+ agent = agent_wrapper.ZaturnAgent(
274
276
  endpoint = state['api_endpoint'],
275
277
  api_key = state['api_key'],
276
- model = state['api_model'],
278
+ model_name = state['api_model'],
277
279
  tools = ZaturnTools(get_active_sources()).tools,
278
280
  image_input = state['api_image_input'],
281
+ reasoning_effort = state['reasoning_effort'],
282
+ system_prompt = state['system_prompt'],
283
+ )
284
+
285
+ chat['messages'] = agent.run(
286
+ prompt = request.form['question'],
287
+ message_history = chat['messages'],
279
288
  )
280
- chat['messages'] = agent.run(chat['messages'])
281
289
  storage.save_chat(slug, chat)
282
- chat = prepare_chat_for_render(chat)
283
-
290
+
284
291
  return boost(
285
- render_template('chat.html', chat=chat),
292
+ render_template('chat.html', chat=prepare_chat_for_render(chat)),
286
293
  push_url = 'false',
287
294
  reswap = 'innerHTML scroll:bottom',
288
295
  )
zaturn/studio/storage.py CHANGED
@@ -14,12 +14,24 @@ STATE_FILE = USER_DATA_DIR / 'studio.json'
14
14
  CHATS_DIR = USER_DATA_DIR / 'chats'
15
15
  os.makedirs(CHATS_DIR, exist_ok=True)
16
16
 
17
+ DEFAULT_PROMPT = """
18
+ You are a helpful data analysis assistant.
19
+ Use only the tool provided data sources to process user inputs.
20
+ Do not use external sources or your own knowledge base.
21
+ Also, the tool outputs are shown to the user.
22
+ So, please avoid repeating the tool outputs in the generated text.
23
+ Use list_data_sources and describe_table whenever needed,
24
+ do not prompt the user for source names and column names.
25
+ """.strip('\n')
26
+
17
27
 
18
28
  def load_state() -> dict:
19
29
  if os.path.exists(STATE_FILE):
20
30
  with open(STATE_FILE) as f:
21
31
  state = json.loads(f.read())
22
32
  state['sources'] = state.get('sources', {})
33
+ state['system_prompt'] = state.get('system_prompt', DEFAULT_PROMPT)
34
+ state['reasoning_effort'] = state.get('reasoning_effort', 'none')
23
35
  return state
24
36
  else:
25
37
  return {}
@@ -51,10 +63,8 @@ def create_chat(question: str):
51
63
 
52
64
  chat = {
53
65
  'slug': slug,
54
- 'messages': [{
55
- 'role': 'user',
56
- 'content': question,
57
- }]
66
+ 'messages': [],
67
+ 'schema_version': 1
58
68
  }
59
69
 
60
70
  filename = CHATS_DIR / f'{slug}.json'
@@ -19,13 +19,13 @@
19
19
  <img src="/static/logo.svg" class="logo">
20
20
  </a>
21
21
  <a href="/">
22
- 💬
22
+ {% include('icons/chat-bubble.svg') %}
23
23
  </a>
24
24
  <a href="/sources/manage" title="Manage Sources">
25
- 📎
25
+ {% include('icons/database.svg') %}
26
26
  </a>
27
27
  <a href="/settings" title="Settings">
28
- 🛠
28
+ {% include('icons/settings.svg') %}
29
29
  </a>
30
30
  </header>
31
31
  {% with slugs=slugs %}
@@ -1,4 +1,3 @@
1
1
  <div class="ai-message">
2
- <!--<p><b class="sender">Zaturn:</b></p>-->
3
- <div>{{msg['html'] | safe}}</div>
2
+ <div>{{part['html_content'] | safe}}</div>
4
3
  </div>
@@ -5,15 +5,15 @@
5
5
  <input type="hidden" name="key" value="{{key}}">
6
6
  {% if active %}
7
7
  <input type="hidden" name="new_status" value="inactive">
8
- <button class="active" title="Currently visible to LLM, Click to Toggle">Active ✅</button>
8
+ <button class="active" title="Currently visible to LLM, Click to Toggle">Active {% include('icons/check-circle-solid.svg') %}</button>
9
9
  {% else %}
10
10
  <input type="hidden" name="new_status" value="active">
11
- <button class="inactive" title="Currently invisible to LLM, Click to Toggle">Inactive 😴</button>
11
+ <button class="inactive" title="Currently invisible to LLM, Click to Toggle">Inactive {% include('icons/warning-triangle.svg') %}</button>
12
12
  {% endif %}
13
13
  </form>
14
14
 
15
15
  <form action="/source/delete" method="POST">
16
16
  <input type="hidden" name="key" value="{{key}}">
17
- <button class="danger">Delete 🗑</button>
17
+ <button class="danger">Delete {% include('icons/trash.svg') %}</button>
18
18
  </form>
19
19
  </div>
@@ -1,22 +1,30 @@
1
1
  <section id="chat">
2
- {% for msg in chat['messages'] %}
3
- {% if msg['role']=='user' %}
4
- {% include('user_message.html') %}
5
- {% elif msg['role']=='assistant' and not msg['is_tool_call'] %}
6
- {% include('ai_message.html') %}
7
- {% elif msg['role']=='tool' %}
8
- {% include('function_call.html') %}
9
- {% endif %}
10
- {% endfor %}
2
+ {% if chat.get('schema_version', 0) >= 1 %}
3
+ {% for message in chat['messages'] %}
4
+ {% for part in message['parts'] %}
5
+ {% if part['part_kind']=='system-prompt' %}
6
+ {% include('chat_metadata.html') %}
7
+ {% elif part['part_kind']=='user-prompt' %}
8
+ {% include('user_message.html') %}
9
+ {% elif part['part_kind']=='text' and message['kind']=='response' %}
10
+ {% include('ai_message.html') %}
11
+ {% elif part['part_kind']=='tool-return' %}
12
+ {% include('function_call.html') %}
13
+ {% endif %}
14
+ {% endfor %}
15
+ {% endfor %}
11
16
 
12
- <form action="/follow_up_message" method="POST">
13
- <input type="hidden" name="slug" value="{{chat['slug']}}">
14
- <textarea
15
- required
16
- name="question"
17
- placeholder="Type a follow up question here."
18
- ></textarea>
19
- <button>➡</button>
20
- </form>
21
- {% include('loader.html') %}
17
+ <form action="/follow_up_message" method="POST">
18
+ <input type="hidden" name="slug" value="{{chat['slug']}}">
19
+ <textarea
20
+ required
21
+ name="question"
22
+ placeholder="Type a follow up question here."
23
+ ></textarea>
24
+ <button>{% include('icons/arrow-right.svg') %}</button>
25
+ </form>
26
+ {% include('loader.html') %}
27
+ {% else %}
28
+ <pre><code>{{chat | tojson(indent=2)}}</code></pre>
29
+ {% endif %}
22
30
  </section>
@@ -0,0 +1,10 @@
1
+ <div class="chat-metadata">
2
+ <dl>
3
+ <dt>Model:</dt>
4
+ <dd>{{chat['messages'][-1]['model_name']}}</dd>
5
+ </dl>
6
+ <details class="system-prompt-message">
7
+ <summary>System Prompt</summary>
8
+ <p>{{part['content']}}</p>
9
+ </details>
10
+ </div>
@@ -15,11 +15,6 @@
15
15
  src: url('/static/fira_code.ttf');
16
16
  }
17
17
 
18
- @font-face {
19
- font-family: "Noto Emoji";
20
- src: url('/static/noto_emoji.ttf');
21
- }
22
-
23
18
  /* Global Styles */
24
19
  * {
25
20
  box-sizing: border-box;
@@ -37,7 +32,7 @@
37
32
  --red: #d43a3a;
38
33
  --fg2: #3a4d74;
39
34
  --fg1: #182030;
40
- --base-size: clamp(16px, 1.4vw, 32px);
35
+ --base-size: clamp(16px, 1.3vw, 32px);
41
36
  --max-width: calc(var(--base-size)*36);
42
37
  font-size: var(--base-size);
43
38
  letter-spacing: 0.02rem;
@@ -55,6 +50,7 @@ body {
55
50
  align-content: stretch;
56
51
  height: 99vh;
57
52
  line-height: 170%;
53
+ overflow-y: hidden;
58
54
  }
59
55
 
60
56
  a {
@@ -80,6 +76,11 @@ header .logo {
80
76
  height: 2rem;
81
77
  }
82
78
 
79
+ header svg {
80
+ width: 1.25rem;
81
+ height: 1.25rem;
82
+ }
83
+
83
84
  #sidebar {
84
85
  grid-area: 2 / 1 / 3 / 2;
85
86
  background: var(--bg0);
@@ -120,12 +121,22 @@ section {
120
121
 
121
122
  section h1 {
122
123
  margin-bottom: 2rem;
124
+ display: flex;
125
+ align-items: center;
126
+ gap: 0.5rem;
127
+ }
128
+
129
+ section h1 svg {
130
+ width: 1.5rem;
131
+ height: 1.5rem;
123
132
  }
124
133
 
125
134
  .btn, button {
126
135
  text-decoration: none;
127
136
  background: var(--bg2);
128
- display: inline-block;
137
+ display: inline-flex;
138
+ align-items: center;
139
+ gap: 0.5rem;
129
140
  padding: 0.5rem 1rem;
130
141
  border-radius: 0.2rem;
131
142
  color: var(--t1);
@@ -145,6 +156,11 @@ section h1 {
145
156
  opacity: 0.8;
146
157
  }
147
158
 
159
+ .btn svg {
160
+ width: 1rem;
161
+ height: 1rem;
162
+ }
163
+
148
164
  code {
149
165
  font-family: "Fira Code", monospace;
150
166
  font-size: 0.8rem;
@@ -160,6 +176,14 @@ code {
160
176
  background: var(--bg0);
161
177
  padding: 0.5rem;
162
178
  border: 1px solid var(--bg2);
179
+ display: flex;
180
+ align-items: center;
181
+ gap: 0.5rem;
182
+ }
183
+
184
+ .alert svg {
185
+ width: 1rem;
186
+ height: 1rem;
163
187
  }
164
188
 
165
189
 
@@ -178,6 +202,9 @@ form label {
178
202
 
179
203
  form small {
180
204
  font-size: 0.8rem;
205
+ }
206
+
207
+ form small:first-child {
181
208
  font-weight: bold;
182
209
  }
183
210
 
@@ -196,8 +223,32 @@ form input:focus {
196
223
  border-bottom: 1px solid var(--t1);
197
224
  }
198
225
 
226
+ form select {
227
+ border: none;
228
+ outline: none;
229
+ background: none;
230
+ font-family: "Fira Code", monospace;
231
+ border-bottom: 1px solid var(--fg1);
232
+ color: var(--fg1);
233
+ font-size: 0.8rem;
234
+ padding: 0.2rem;
235
+ }
236
+
237
+ form select:focus {
238
+ color: var(--t1);
239
+ border-bottom: 1px solid var(--t1);
240
+ }
241
+
199
242
  form button {
200
243
  justify-self: start;
244
+ display: inline-flex;
245
+ align-items: center;
246
+ gap: 0.5rem;
247
+ }
248
+
249
+ form button svg {
250
+ width: 1.25rem;
251
+ height: 1.25rem;
201
252
  }
202
253
 
203
254
  form progress[value="0"] {
@@ -205,12 +256,17 @@ form progress[value="0"] {
205
256
  }
206
257
 
207
258
  form textarea {
208
- font-size: inherit;
259
+ font-family: "Fira Code", monospace;
260
+ font-size: 0.8rem;
209
261
  padding: 0.5rem;
210
262
  border: none;
211
263
  outline: none;
212
264
  }
213
265
 
266
+ form textarea.large {
267
+ height: 7rem;
268
+ }
269
+
214
270
  form.htmx-request {
215
271
  display: none;
216
272
  }
@@ -224,7 +280,14 @@ form ~ .loader {
224
280
  }
225
281
 
226
282
  form.htmx-request + .loader {
227
- display: block;
283
+ display: flex;
284
+ align-items: center;
285
+ gap: 0.5rem;
286
+ }
287
+
288
+ form ~ .loader svg {
289
+ width: 1.2rem;
290
+ height: 1.2rem;
228
291
  }
229
292
 
230
293
  @keyframes loadanim {
@@ -265,12 +328,19 @@ form.htmx-request + .loader {
265
328
  #manage-sources .source-card button {
266
329
  font-size: 0.75rem;
267
330
  padding: 0.25rem 0.5rem;
331
+ display: inline-flex;
332
+ align-items: center;
268
333
  }
269
334
 
270
335
  #manage-sources .source-card button.inactive {
271
336
  filter: grayscale(100%);
272
337
  }
273
338
 
339
+ #manage-sources .source-card button svg {
340
+ width: 0.75rem;
341
+ height: 0.75rem;
342
+ }
343
+
274
344
  #manage-sources .row-c2 {
275
345
  margin-top: 1.5rem;
276
346
  }
@@ -301,10 +371,29 @@ form.htmx-request + .loader {
301
371
  padding: 1.5rem 0.5rem;
302
372
  }
303
373
 
304
- #chat ul {
374
+ #chat ul, #chat ol {
305
375
  list-style-position: inside;
306
376
  }
307
377
 
378
+ #chat .chat-metadata * {
379
+ font-family: "Fira Code", monospace;
380
+ font-size: 0.9rem;
381
+ }
382
+
383
+ #chat dt, #chat dd {
384
+ display: inline-block;
385
+ }
386
+
387
+ #chat dt {
388
+ font-weight: 700;
389
+ }
390
+
391
+
392
+ #chat .system-prompt-message summary {
393
+ cursor: pointer;
394
+ margin: 1rem 0;
395
+ }
396
+
308
397
  #chat .user-message {
309
398
  display: grid;
310
399
  grid-template-columns: auto 1fr;
@@ -314,13 +403,26 @@ form.htmx-request + .loader {
314
403
  }
315
404
 
316
405
  #chat .user-message .sender {
406
+ font-size: 75%;
407
+ display: inline-flex;
408
+ align-items: center;
409
+ gap: 0.5rem;
410
+ background: none;
411
+ border: 0.1rem solid var(--t1);
412
+ padding: 0.125rem 0.75rem;
413
+ border-radius: 2rem;
317
414
  color: var(--t1);
318
- border: 1px solid var(--t1);
319
- padding: 0.125rem 0.5rem;
320
- font-size: 70%;
415
+ margin-right: 0.5rem;
321
416
  }
322
417
 
418
+ #chat .user-message .sender svg {
419
+ width: 1rem;
420
+ height: 1rem;
421
+ }
323
422
 
423
+ #chat .user-message>p {
424
+ padding-top: 0.25rem;
425
+ }
324
426
 
325
427
  #chat .ai-message {
326
428
  display: grid;
@@ -332,30 +434,51 @@ form.htmx-request + .loader {
332
434
  font-size: 70%;
333
435
  }
334
436
 
437
+ #chat .ai-message>div>* {
438
+ margin: 0.25rem 0;
439
+ }
440
+
335
441
 
336
442
  #chat .function-call {
337
443
  display: grid;
338
444
  gap: 0.75rem;
339
445
  align-items: center;
446
+ grid-template-columns: auto 1fr;
340
447
  }
341
448
 
342
- #chat .function-name b {
449
+ #chat .function-name {
343
450
  font-size: 75%;
344
- display: inline-block;
451
+ justify-self: start;
452
+ display: inline-flex;
453
+ align-items: center;
454
+ gap: 0.5rem;
345
455
  background: var(--t1);
346
- padding: 0rem 0.5rem;
456
+ border: 0.1rem solid var(--t1);
457
+ padding: 0.125rem 0.75rem;
347
458
  border-radius: 2rem;
348
459
  color: var(--bg0);
349
460
  margin-right: 0.5rem;
350
461
  }
351
462
 
352
- #chat .function-call pre {
353
- overflow-x: auto;
463
+ #chat .function-name svg {
464
+ width: 1rem;
465
+ height: 1rem;
354
466
  }
355
467
 
356
- #chat .function-name code {
357
- color: var(--t1);
358
- font-weight: 700;
468
+ #chat .function-call .exec-time {
469
+ display: inline-flex;
470
+ align-items: center;
471
+ gap: 0.5rem;
472
+ font-size: 0.8rem;
473
+ }
474
+
475
+ #chat .function-call .exec-time svg {
476
+ width: 0.8rem;
477
+ height: 0.8rem;
478
+ }
479
+
480
+ #chat .function-call pre {
481
+ overflow-x: auto;
359
482
  }
360
483
 
361
484
  #chat .function-call .args {
@@ -365,10 +488,12 @@ form.htmx-request + .loader {
365
488
  font-family: Fira Code, monospace;
366
489
  font-size: 75%;
367
490
  line-height: 180%;
491
+ grid-column: 1 / -1;
368
492
  }
369
493
 
370
494
  #chat .function-output {
371
495
  overflow-x: auto;
496
+ grid-column: 1 / -1;
372
497
  }
373
498
 
374
499
  #chat table {
@@ -1,7 +1,8 @@
1
1
  <div class="function-call">
2
- <pre class="function-name"><b>fn_call</b><code>{{msg['call_details']['name']}}</code></pre>
3
- {% if msg['call_details']['arguments'] %}
4
- <p class="args">{{msg['call_details']['arguments'] | safe}}</p>
2
+ <b class="function-name">{% include('icons/wrench.svg') %} {{part['call_details']['name']}}</b>
3
+ <b class="exec-time">{% include('icons/timer.svg') %} {{part['call_details']['exec_time']}}s</b>
4
+ {% if part['call_details']['args_html'] %}
5
+ <p class="args">{{part['call_details']['args_html'] | safe}}</p>
5
6
  {% endif %}
6
- <div class="function-output">{{ msg['html'] | safe}}</div>
7
+ <div class="function-output">{{ part['html_content'] | safe}}</div>
7
8
  </div>
@@ -0,0 +1,3 @@
1
+ <svg viewBox="0 0 24 24" stroke-width="1.5" fill="none">
2
+ <path d="M21 12L3 12M3 12L11.5 3.5M3 12L11.5 20.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg viewBox="0 0 24 24" stroke-width="1.5" fill="none">
2
+ <path d="M3 12L21 12M21 12L12.5 3.5M21 12L12.5 20.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
3
+ </svg>
@@ -0,0 +1,6 @@
1
+ <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none">
2
+ <path d="M17 12.5C17.2761 12.5 17.5 12.2761 17.5 12C17.5 11.7239 17.2761 11.5 17 11.5C16.7239 11.5 16.5 11.7239 16.5 12C16.5 12.2761 16.7239 12.5 17 12.5Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
3
+ <path d="M12 12.5C12.2761 12.5 12.5 12.2761 12.5 12C12.5 11.7239 12.2761 11.5 12 11.5C11.7239 11.5 11.5 11.7239 11.5 12C11.5 12.2761 11.7239 12.5 12 12.5Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
4
+ <path d="M7 12.5C7.27614 12.5 7.5 12.2761 7.5 12C7.5 11.7239 7.27614 11.5 7 11.5C6.72386 11.5 6.5 11.7239 6.5 12C6.5 12.2761 6.72386 12.5 7 12.5Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
5
+ <path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 13.8214 2.48697 15.5291 3.33782 17L2.5 21.5L7 20.6622C8.47087 21.513 10.1786 22 12 22Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
6
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg viewBox="0 0 24 24" fill="none">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M12 1.25C6.06294 1.25 1.25 6.06294 1.25 12C1.25 17.9371 6.06294 22.75 12 22.75C17.9371 22.75 22.75 17.9371 22.75 12C22.75 6.06294 17.9371 1.25 12 1.25ZM7.53044 11.9697C7.23755 11.6768 6.76268 11.6768 6.46978 11.9697C6.17689 12.2626 6.17689 12.7374 6.46978 13.0303L9.46978 16.0303C9.76268 16.3232 10.2376 16.3232 10.5304 16.0303L17.5304 9.03033C17.8233 8.73744 17.8233 8.26256 17.5304 7.96967C17.2375 7.67678 16.7627 7.67678 16.4698 7.96967L10.0001 14.4393L7.53044 11.9697Z" fill="currentColor" stroke-width="1.5"></path>
3
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg width="24px" height="24px" viewBox="0 0 24 24" fill="none">
2
+ <path d="M5 12V18C5 18 5 21 12 21C19 21 19 18 19 18V12" stroke="currentColor" stroke-width="1.5"></path>
3
+ <path d="M5 6V12C5 12 5 15 12 15C19 15 19 12 19 12V6" stroke="currentColor" stroke-width="1.5"></path>
4
+ <path d="M12 3C19 3 19 6 19 6C19 6 19 9 12 9C5 9 5 6 5 6C5 6 5 3 12 3Z" stroke="currentColor" stroke-width="1.5"></path>
5
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg stroke-width="1.5" viewBox="0 0 24 24" fill="none">
2
+ <path d="M8 18C8 20.4148 9.79086 21 12 21C15.7587 21 17 18.5 14.5 13.5C11 18 10.5 11 11 9C9.5 12 8 14.8177 8 18Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
3
+ <path d="M12 21C17.0495 21 20 18.0956 20 13.125C20 8.15444 12 3 12 3C12 3 4 8.15444 4 13.125C4 18.0956 6.95054 21 12 21Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
4
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg stroke-width="1.5" viewBox="0 0 24 24" fill="none">
2
+ <path d="M3 19V5C3 3.89543 3.89543 3 5 3H16.1716C16.702 3 17.2107 3.21071 17.5858 3.58579L20.4142 6.41421C20.7893 6.78929 21 7.29799 21 7.82843V19C21 20.1046 20.1046 21 19 21H5C3.89543 21 3 20.1046 3 19Z" stroke="currentColor" stroke-width="1.5"></path>
3
+ <path d="M8.6 9H15.4C15.7314 9 16 8.73137 16 8.4V3.6C16 3.26863 15.7314 3 15.4 3H8.6C8.26863 3 8 3.26863 8 3.6V8.4C8 8.73137 8.26863 9 8.6 9Z" stroke="currentColor" stroke-width="1.5"></path>
4
+ <path d="M6 13.6V21H18V13.6C18 13.2686 17.7314 13 17.4 13H6.6C6.26863 13 6 13.2686 6 13.6Z" stroke="currentColor" stroke-width="1.5"></path>
5
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg viewBox="0 0 24 24" fill="none">
2
+ <path d="M14 11.9976C14 9.5059 11.683 7 8.85714 7C8.52241 7 7.41904 7.00001 7.14286 7.00001C4.30254 7.00001 2 9.23752 2 11.9976C2 14.376 3.70973 16.3664 6 16.8714C6.36756 16.9525 6.75006 16.9952 7.14286 16.9952" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
3
+ <path d="M10 11.9976C10 14.4893 12.317 16.9952 15.1429 16.9952C15.4776 16.9952 16.581 16.9952 16.8571 16.9952C19.6975 16.9952 22 14.7577 22 11.9976C22 9.6192 20.2903 7.62884 18 7.12383C17.6324 7.04278 17.2499 6.99999 16.8571 6.99999" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
4
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M6.90588 4.53682C6.50592 4.2998 6 4.58808 6 5.05299V18.947C6 19.4119 6.50592 19.7002 6.90588 19.4632L18.629 12.5162C19.0211 12.2838 19.0211 11.7162 18.629 11.4838L6.90588 4.53682Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
3
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg viewBox="0 0 24 24" fill="none">
2
+ <path d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
3
+ <path d="M19.6224 10.3954L18.5247 7.7448L20 6L18 4L16.2647 5.48295L13.5578 4.36974L12.9353 2H10.981L10.3491 4.40113L7.70441 5.51596L6 4L4 6L5.45337 7.78885L4.3725 10.4463L2 11V13L4.40111 13.6555L5.51575 16.2997L4 18L6 20L7.79116 18.5403L10.397 19.6123L11 22H13L13.6045 19.6132L16.2551 18.5155C16.6969 18.8313 18 20 18 20L20 18L18.5159 16.2494L19.6139 13.598L21.9999 12.9772L22 11L19.6224 10.3954Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
4
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg stroke-width="1.5" viewBox="0 0 24 24" fill="none">
2
+ <path d="M9 2L15 2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
3
+ <path d="M12 10L12 14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
4
+ <path d="M12 22C16.4183 22 20 18.4183 20 14C20 9.58172 16.4183 6 12 6C7.58172 6 4 9.58172 4 14C4 18.4183 7.58172 22 12 22Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
5
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg viewBox="0 0 24 24" stroke-width="1.5" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M20 9L18.005 20.3463C17.8369 21.3026 17.0062 22 16.0353 22H7.96474C6.99379 22 6.1631 21.3026 5.99496 20.3463L4 9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
3
+ <path d="M21 6L15.375 6M3 6L8.625 6M8.625 6V4C8.625 2.89543 9.52043 2 10.625 2H13.375C14.4796 2 15.375 2.89543 15.375 4V6M8.625 6L15.375 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg viewBox="0 0 24 24" fill="none">
2
+ <path d="M6 20L18 20" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
3
+ <path d="M12 16V4M12 4L15.5 7.5M12 4L8.5 7.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg stroke-width="1.5" viewBox="0 0 24 24" fill="none">
2
+ <path d="M5 20V19C5 15.134 8.13401 12 12 12V12C15.866 12 19 15.134 19 19V20" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
3
+ <path d="M12 12C14.2091 12 16 10.2091 16 8C16 5.79086 14.2091 4 12 4C9.79086 4 8 5.79086 8 8C8 10.2091 9.79086 12 12 12Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
4
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M20.0429 21H3.95705C2.41902 21 1.45658 19.3364 2.22324 18.0031L10.2662 4.01533C11.0352 2.67792 12.9648 2.67791 13.7338 4.01532L21.7768 18.0031C22.5434 19.3364 21.581 21 20.0429 21Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path>
3
+ <path d="M12 9V13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path>
4
+ <path d="M12 17.01L12.01 16.9989" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
5
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg viewBox="0 0 24 24" stroke-width="1.5" fill="none">
2
+ <path d="M10.0503 10.6066L2.97923 17.6777C2.19818 18.4587 2.19818 19.725 2.97923 20.5061V20.5061C3.76027 21.2871 5.0266 21.2871 5.80765 20.5061L12.8787 13.435" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
3
+ <path d="M10.0502 10.6066C9.20638 8.45358 9.37134 5.6286 11.1109 3.88909C12.8504 2.14957 16.0606 1.76777 17.8284 2.82843L14.7877 5.8691L14.5051 8.98014L17.6161 8.69753L20.6568 5.65685C21.7175 7.42462 21.3357 10.6349 19.5961 12.3744C17.8566 14.1139 15.0316 14.2789 12.8786 13.435" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
4
+ </svg>
@@ -1 +1 @@
1
- <p class="loader">💆‍♀️ Relax, Zaturn is running...</p>
1
+ <p class="loader">{% include('icons/play.svg') %} Relax, Zaturn is running...</p>
@@ -1,13 +1,13 @@
1
1
  <section id="manage-sources">
2
2
  {% if sources %}
3
- <h1>🔗 Your Data Sources</h1>
3
+ <h1>{% include('icons/database.svg') %} Your Data Sources</h1>
4
4
  {% for key in sources %}
5
5
  {% with key=key, active=sources[key]['active'] %}
6
6
  {% include('c_source_card.html') %}
7
7
  {% endwith %}
8
8
  {% endfor %}
9
9
  {% else %}
10
- <h1>➕ Add Some Data</h1>
10
+ <h1>{% include('icons/database.svg') %} Add Some Data</h1>
11
11
  {% endif %}
12
12
 
13
13
  <div class="row-c2">
@@ -22,7 +22,7 @@
22
22
  <input type="file" name="datafile" accept=".csv,.db,.sqlite,.sqlite3,.duckdb,.parquet,.pq" placeholder="" required>
23
23
  <small><em>CSV / SQLite / DuckDB / Parquet</em></small>
24
24
  </label>
25
- <button>⬆ Upload</button>
25
+ <button>{% include('icons/upload.svg') %} Upload</button>
26
26
  <progress value='0' max='100'></progress>
27
27
  </form>
28
28
 
@@ -36,9 +36,9 @@
36
36
  <label>
37
37
  <small>Or Link A Database Using URL</small>
38
38
  <input type="text" name="db_url" required>
39
- <small><em>PostgreSQL / MySQL / Clickhouse</em></small>
39
+ <small><em>PostgreSQL / MySQL / Clickhouse / MSSQL</em></small>
40
40
  </label>
41
- <button>🔗 Connect</button>
41
+ <button>{% include('icons/link.svg') %} Connect</button>
42
42
  </form>
43
43
  </div>
44
44
 
@@ -1,5 +1,5 @@
1
1
  <section id="new-conversation">
2
- <h1>Vamos! ᯓ★</h1>
2
+ <h1>Vamos!</h1>
3
3
  <p>An AMA Session With Your Data</p>
4
4
  <form action="/create_new_chat" method="POST">
5
5
  <textarea
@@ -7,7 +7,7 @@
7
7
  name="question"
8
8
  placeholder="Type your question here...&#10E.g.: Explore the linked data and tell me something useful."
9
9
  ></textarea>
10
- <button>➡</button>
10
+ <button>{% include('icons/arrow-right.svg') %}</button>
11
11
  </form>
12
12
  {% include('loader.html') %}
13
13
  </section>
@@ -1,13 +1,13 @@
1
1
  <section id="settings">
2
2
  <h1>
3
- <a href="/">⬅</a>
3
+ <a href="/">{% include('icons/arrow-left.svg') %}</a>
4
4
  Settings
5
5
  </h1>
6
6
 
7
7
  {% if updated %}
8
8
  <p class="alert">
9
9
  <code>{{updated}}</code>
10
- Settings Updated!
10
+ {% include('icons/check-circle-solid.svg') %} Settings Updated!
11
11
  </p>
12
12
  {% endif %}
13
13
 
@@ -24,6 +24,27 @@
24
24
  <small>OPENAI API ENDPOINT (Leave blank to use default)</small>
25
25
  <input type="text" name="api_endpoint" value="{{current['api_endpoint']}}">
26
26
  </label>
27
- <button>💾 Save</button>
27
+ <label>
28
+ <small>REASONING EFFORT</small>
29
+ <select name="reasoning_effort">
30
+ <option value="none" {% if current['reasoning_effort']=='none' %}selected{% endif %}>
31
+ none
32
+ </option>
33
+ <option value="low" {% if current['reasoning_effort']=='low' %}selected{% endif %}>
34
+ low
35
+ </option>
36
+ <option value="medium" {% if current['reasoning_effort']=='medium' %}selected{% endif %}>
37
+ medium
38
+ </option>
39
+ <option value="high" {% if current['reasoning_effort']=='high' %}selected{% endif %}>
40
+ high
41
+ </option>
42
+ </select>
43
+ </label>
44
+ <label>
45
+ <small>SYSTEM PROMPT</small>
46
+ <textarea name="system_prompt" class="large" required>{{current['system_prompt']}}</textarea>
47
+ </label>
48
+ <button>{% include('icons/floppy-disk.svg') %} Save</button>
28
49
  </form>
29
50
  </section>
@@ -1,6 +1,7 @@
1
1
  <section id="setup-prompt">
2
- <h1>Are You Ready? 🔥</h1>
2
+ <h1>Are You Ready? {% include('icons/fire-flame.svg') %}</h1>
3
3
  <p>We need to set you up though.</p>
4
4
  <p>Head over to <a href="/settings">settings</a> and add your LLM API Key.</p>
5
- <p><a href="/settings" class="btn">⚙ Open Settings</a>
5
+ <br>
6
+ <p><a href="/settings" class="btn">{% include('icons/settings.svg') %} Open Settings</a>
6
7
  </section>
@@ -1,4 +1,4 @@
1
1
  <div class="user-message">
2
- <b class="sender">You</b>
3
- <p>{{msg['content']}}</p>
2
+ <b class="sender">{% include('icons/user.svg') %} User</b>
3
+ <p>{{part['content']}}</p>
4
4
  </div>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zaturn
3
- Version: 0.2.2
3
+ Version: 0.3.0
4
4
  Summary: AI Data Analysis MCP & Studio
5
5
  Author-email: Karthik Devan <krtdvn@gmail.com>
6
6
  Maintainer-email: Karthik Devan <krtdvn@gmail.com>
@@ -18,13 +18,14 @@ Requires-Dist: function-schema>=0.4.5
18
18
  Requires-Dist: kaleido==0.2.1
19
19
  Requires-Dist: mistune>=3.1.3
20
20
  Requires-Dist: openai>=1.82.1
21
- Requires-Dist: openai-agents>=0.0.16
22
21
  Requires-Dist: pandas>=2.2.3
23
22
  Requires-Dist: pillow>=11.2.1
24
23
  Requires-Dist: platformdirs>=4.3.7
25
24
  Requires-Dist: plotly[express]>=6.0.1
26
25
  Requires-Dist: psycopg2-binary>=2.9.10
27
26
  Requires-Dist: pyarrow>=19.0.1
27
+ Requires-Dist: pydantic-ai>=0.8.0
28
+ Requires-Dist: pydantic-core>=2.33.2
28
29
  Requires-Dist: pymssql>=2.3.7
29
30
  Requires-Dist: pymysql>=1.1.1
30
31
  Requires-Dist: python-lsp-server>=1.12.2
@@ -43,7 +44,7 @@ Dynamic: license-file
43
44
  </h1>
44
45
 
45
46
  <a href="https://discord.gg/K8mECeVzpQ">
46
- <img src="https://zaturn.pro/assets/discord-full.png" height="20" width="133" alt="Discord Logo">
47
+ Join the Discord
47
48
  </a>
48
49
 
49
50
  ## Just Chat With Your Data! No SQL, No Python.
@@ -0,0 +1,55 @@
1
+ zaturn/mcp/__init__.py,sha256=8pTcOyJ3tK7TJcP4HBDVQA_MVuMrfYJwa8sWKfCvXIE,3466
2
+ zaturn/studio/__init__.py,sha256=A1QxWydtsE0VmsJlvF7O9Fx1-WKjV3MBPje7knFSDzw,83
3
+ zaturn/studio/agent_wrapper.py,sha256=HjWieqZkTWhei41c1SfLzNK-UcZwxy-jedcHUk31Vt4,1771
4
+ zaturn/studio/app.py,sha256=GtlxapusQCPk4fmUAkxemnytqk3BGkg6Oc0bQmlET0c,9747
5
+ zaturn/studio/storage.py,sha256=YsE98yQ3hPsYvbuWPyc_MqvjzbNT8kv8HqhCOSbC5bA,2588
6
+ zaturn/studio/static/fira_code.ttf,sha256=mqZDedYDyifatZw-V4E2gnkwNfVs5GgvudgTKEd_0UQ,264848
7
+ zaturn/studio/static/inter_ital_var.ttf,sha256=YTb3M3L-3De4D9Go7D4hc0Bz_xc3bzZycY8YZ3lnLno,904532
8
+ zaturn/studio/static/inter_var.ttf,sha256=C-I5nqkl8fg_-XR2R2HamGDsUHQu0ppdTB_9DFx6w6g,874708
9
+ zaturn/studio/static/logo.png,sha256=QNyNJSy_KFdPmHoocqkynN8yp73vFqqcMthPmNGLPg8,20916
10
+ zaturn/studio/static/logo.svg,sha256=UH5m-H2YZy54eJYKd_zM8xeEWntOaYLfrs4MmGw2MUI,588
11
+ zaturn/studio/static/js/htmx-multi-swap.js,sha256=It1Qs3c1QcnlBLN0kwbMAE0U_Ww5oxeUdMJFdIqbRwU,1480
12
+ zaturn/studio/static/js/htmx.min.js,sha256=acrv0NqSJpBm5yXX_hdeJrnVDJYuMFZFnAxHcVTNudM,50918
13
+ zaturn/studio/templates/_shell.html,sha256=reh4GSnR64jwlMe9iT4bknddl-pvTcPkThFsT2CTJNU,1157
14
+ zaturn/studio/templates/ai_message.html,sha256=fSskULhMrGiPHYQHC4dkyZjUYqmP8zAl3Tq4WUG17ME,77
15
+ zaturn/studio/templates/c_settings_updated.html,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
16
+ zaturn/studio/templates/c_source_card.html,sha256=14Mer1f_7l5uRGcqtxnaocBZCy4uVFGsxdLpcUUwzn4,836
17
+ zaturn/studio/templates/chat.html,sha256=c2hDmNsuQwvOrvd9Y2BWHA1813takHE7kG4VidYFaxw,1071
18
+ zaturn/studio/templates/chat_metadata.html,sha256=mWh506QnVc8sltonspXuG_Ld-K2J4eJKfHSmhY0WBHA,245
19
+ zaturn/studio/templates/function_call.html,sha256=4tOqFBs6agLSzh2l54cfpWMbRA4V0-mqhgb0S9hC5Yw,430
20
+ zaturn/studio/templates/loader.html,sha256=8rx0RjAmhJXCcjrtComCkAdZ6_13-Yjf17K-fdpRaQk,82
21
+ zaturn/studio/templates/manage_sources.html,sha256=JtyYZ_cO_Da2pcvMjpmA4WfG1z9-G6h3_NA74r9WBdg,1478
22
+ zaturn/studio/templates/nav.html,sha256=6wB7zO-DEsJn_-ZUujq8iIoYi5fFeYUJoMPnix320Q8,104
23
+ zaturn/studio/templates/new_conversation.html,sha256=UU7X7Mrj_qltrSUJWhuUPlGEjjmiY3Ihjd8FvjuEWoc,431
24
+ zaturn/studio/templates/settings.html,sha256=x3P8U6hSD5tj-qY__7Lc0kZ5GZZ94Modnytl3IzTDjs,1659
25
+ zaturn/studio/templates/setup_prompt.html,sha256=W9ZCizvSHllMub_SBvib24MhNIDh-DWEbaY-2Q4t7I0,321
26
+ zaturn/studio/templates/user_message.html,sha256=ZeV6Lw776xNsnCzm2--cEtOil1slrOPLvwpTq7bSMmo,124
27
+ zaturn/studio/templates/css/style.css,sha256=M3s1HTR_137i_FSiLRHMzvCPRjds8MppcZHevBTV0JY,8217
28
+ zaturn/studio/templates/icons/arrow-left.svg,sha256=S-ecMUbtLs-WFklwKQRQDJMDZZ4tZTKZ2AElGiigCNs,213
29
+ zaturn/studio/templates/icons/arrow-right.svg,sha256=FtNCDvdmCLq7lPm9IvgA8iZbVJqHQfzyTWx0zg-aFqA,215
30
+ zaturn/studio/templates/icons/chat-bubble.svg,sha256=kJ4jMNTjWbJNS1kGVcM6Haz0uG_eDQ7Pjp0u6aX2uEo,1116
31
+ zaturn/studio/templates/icons/check-circle-solid.svg,sha256=0YulODKxiD2iPyXpjoBCAo0wNOX1TrRW3sVVGMBYjb0,617
32
+ zaturn/studio/templates/icons/database.svg,sha256=xYO7a-qFwITKuTcbE8bNvFDPGfF4ZOfMjUdRp_hu5os,407
33
+ zaturn/studio/templates/icons/fire-flame.svg,sha256=V7t9todUvxl4A5AwLAAVsiIFBRwodGnWbtHgUBSzis4,496
34
+ zaturn/studio/templates/icons/floppy-disk.svg,sha256=-rsUVlP_iiJT4XYfckGuPgWq1XRclK4LZSrQq43xRgE,657
35
+ zaturn/studio/templates/icons/link.svg,sha256=K3XPHp-e9TnjFqJU0bS8vgTXX8lHkzStgDh2wjqMbf4,687
36
+ zaturn/studio/templates/icons/play.svg,sha256=zRuQPv2bO7qABFpM8Yesfh-k66Y4a4R3qsrpz71L7iw,387
37
+ zaturn/studio/templates/icons/settings.svg,sha256=30EYP16HjWxDWWm73FiCl2ak-e0Dso-sQkIX07VlnU8,763
38
+ zaturn/studio/templates/icons/timer.svg,sha256=-X6UYIG6iWLLPyMP1t7WgLFtdSZD6a3hSFah12uVit0,525
39
+ zaturn/studio/templates/icons/trash.svg,sha256=_10g_X4vqTt677KyAJtXXQD1FbuH95iQ-MqhcGhEvfE,558
40
+ zaturn/studio/templates/icons/upload.svg,sha256=dJsJOF9-Xtv7d8nTOL17kWAb6YrAE2qUrEOYEBGgfV8,309
41
+ zaturn/studio/templates/icons/user.svg,sha256=vtj4RtCtxW5WaMmlBhtMCollHJe-VpFGfLmBlGYSm8k,459
42
+ zaturn/studio/templates/icons/warning-triangle.svg,sha256=3K1lMfgIKFQ7Y1uJbORzVnzaou1jwmVcdeAmtGe_zQg,589
43
+ zaturn/studio/templates/icons/wrench.svg,sha256=275gpXZNLEuzZpUgp6oNTBvl7LzWX-xfTPrcDVxIv1g,700
44
+ zaturn/tools/__init__.py,sha256=vOcHneRuZll9k-2e3sra7m8qfdc4IoAFYc2g_xK79HE,262
45
+ zaturn/tools/config.py,sha256=Ia-WzK_G6vQqB7norvaffb3fKLjtmXupVqAWsxdu6Gs,354
46
+ zaturn/tools/core.py,sha256=4fHxSIbO22cTIejJk02GzUBfWimh1Ql9mZ3JgI1qLBY,2893
47
+ zaturn/tools/query_utils.py,sha256=OnXbONmEgsHcScrJ76b7P3BRLOKgi8TMkhxBcLQK9cY,5306
48
+ zaturn/tools/visualizations.py,sha256=aPXCN1YIjCz6uiwJjduKCiLLtnBFX92hTvjy5EqQYk8,9509
49
+ zaturn/tools/example_data/all_pokemon_data.csv,sha256=SUlGHHWbehuLg-ch1YUrQ6-xBtqHGw6rIkyn70fAgCk,130893
50
+ zaturn-0.3.0.dist-info/licenses/LICENSE,sha256=mZSuFlbEBZGl0-8ULRMLdRDbhau5hrWRNQOjytYeaug,1070
51
+ zaturn-0.3.0.dist-info/METADATA,sha256=K_PMEFwYGg1FHelUgTkLrGd4uV4uAqQX5sa3rdxO4ak,3931
52
+ zaturn-0.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
53
+ zaturn-0.3.0.dist-info/entry_points.txt,sha256=MWMWX0dE_ZQM4StGKCLxJym_D91F82RcBi2LBj0hEho,82
54
+ zaturn-0.3.0.dist-info/top_level.txt,sha256=KLUnwQwVZkfd5YCnnqR35MOOs8KLhanPGelvmRo2MVA,7
55
+ zaturn-0.3.0.dist-info/RECORD,,
Binary file
@@ -1,39 +0,0 @@
1
- zaturn/mcp/__init__.py,sha256=8pTcOyJ3tK7TJcP4HBDVQA_MVuMrfYJwa8sWKfCvXIE,3466
2
- zaturn/studio/__init__.py,sha256=A1QxWydtsE0VmsJlvF7O9Fx1-WKjV3MBPje7knFSDzw,83
3
- zaturn/studio/agent_wrapper.py,sha256=4mEMRfE1Tfv63D7OIabqUbEJdIgO2yo8UXO6PKvNrmI,4603
4
- zaturn/studio/app.py,sha256=LEoSjs5wpZGX1B7pcJ2e_6CaxOFjbO6JitxlkJv__30,9208
5
- zaturn/studio/storage.py,sha256=4fZ_pwc8goe_tKZcYSxHg0lOy79OFn8TgxTroxBmbLs,2041
6
- zaturn/studio/static/fira_code.ttf,sha256=mqZDedYDyifatZw-V4E2gnkwNfVs5GgvudgTKEd_0UQ,264848
7
- zaturn/studio/static/inter_ital_var.ttf,sha256=YTb3M3L-3De4D9Go7D4hc0Bz_xc3bzZycY8YZ3lnLno,904532
8
- zaturn/studio/static/inter_var.ttf,sha256=C-I5nqkl8fg_-XR2R2HamGDsUHQu0ppdTB_9DFx6w6g,874708
9
- zaturn/studio/static/logo.png,sha256=QNyNJSy_KFdPmHoocqkynN8yp73vFqqcMthPmNGLPg8,20916
10
- zaturn/studio/static/logo.svg,sha256=UH5m-H2YZy54eJYKd_zM8xeEWntOaYLfrs4MmGw2MUI,588
11
- zaturn/studio/static/noto_emoji.ttf,sha256=tX7Yla6dCbp7SxnDQ6dc85qtVxVsNFDjOdnRFVbX7cE,890608
12
- zaturn/studio/static/js/htmx-multi-swap.js,sha256=It1Qs3c1QcnlBLN0kwbMAE0U_Ww5oxeUdMJFdIqbRwU,1480
13
- zaturn/studio/static/js/htmx.min.js,sha256=acrv0NqSJpBm5yXX_hdeJrnVDJYuMFZFnAxHcVTNudM,50918
14
- zaturn/studio/templates/_shell.html,sha256=4WxTSo8OoqEtyoUJoeb0BlP3j4ZSpVxunPNndVa7yYc,1061
15
- zaturn/studio/templates/ai_message.html,sha256=-lFJmjxs5QHeIQLfx1-he4zamMO5U7enco8DPGoOItE,114
16
- zaturn/studio/templates/c_settings_updated.html,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
17
- zaturn/studio/templates/c_source_card.html,sha256=TOFssoWHc9pJEWW-GpUGWu4_wZMfJFoFWNtIj3x_LtQ,727
18
- zaturn/studio/templates/chat.html,sha256=29I8R8bti_qMzU9FjBOGiLVEh-g1Es1VbaN6Dj_OMBA,660
19
- zaturn/studio/templates/function_call.html,sha256=5NBzHE9KYlOgmNqv_Nz2PwsNVVhpSVM9NebhTMpAttg,316
20
- zaturn/studio/templates/loader.html,sha256=jkrpmPA3_P0aL0ZrE_wkoGlnK12Go-OI2xtUyCDRikQ,64
21
- zaturn/studio/templates/manage_sources.html,sha256=mLyNAYNu-u0mjTH8ybYO8HCYhHP6NVao20jNyqevu9g,1350
22
- zaturn/studio/templates/nav.html,sha256=6wB7zO-DEsJn_-ZUujq8iIoYi5fFeYUJoMPnix320Q8,104
23
- zaturn/studio/templates/new_conversation.html,sha256=p0e8D7b_riARS6xmqGk-g5tITd_ikgggVWWWlZ8MN5E,403
24
- zaturn/studio/templates/settings.html,sha256=ZmukMbTsGGsVauEg9EtyyiR2_fOYvO5fAEAiJL0CJnU,760
25
- zaturn/studio/templates/setup_prompt.html,sha256=K8l3ewlEyrUFiMmw2qPyDFbFNkq1RCBksrw9eL2tkIs,249
26
- zaturn/studio/templates/user_message.html,sha256=WbvdVr4AVzVx6MG0eQ_ulMeHB9rpScZc-EZlCoYFEgw,90
27
- zaturn/studio/templates/css/style.css,sha256=cKImLAjLp6XjHdT16v9yJS0DHG2wWx8djR18SHfMqvE,6264
28
- zaturn/tools/__init__.py,sha256=vOcHneRuZll9k-2e3sra7m8qfdc4IoAFYc2g_xK79HE,262
29
- zaturn/tools/config.py,sha256=Ia-WzK_G6vQqB7norvaffb3fKLjtmXupVqAWsxdu6Gs,354
30
- zaturn/tools/core.py,sha256=4fHxSIbO22cTIejJk02GzUBfWimh1Ql9mZ3JgI1qLBY,2893
31
- zaturn/tools/query_utils.py,sha256=OnXbONmEgsHcScrJ76b7P3BRLOKgi8TMkhxBcLQK9cY,5306
32
- zaturn/tools/visualizations.py,sha256=aPXCN1YIjCz6uiwJjduKCiLLtnBFX92hTvjy5EqQYk8,9509
33
- zaturn/tools/example_data/all_pokemon_data.csv,sha256=SUlGHHWbehuLg-ch1YUrQ6-xBtqHGw6rIkyn70fAgCk,130893
34
- zaturn-0.2.2.dist-info/licenses/LICENSE,sha256=mZSuFlbEBZGl0-8ULRMLdRDbhau5hrWRNQOjytYeaug,1070
35
- zaturn-0.2.2.dist-info/METADATA,sha256=BSnrqH9k7MNfqmRVPEO6q3884y-8Ym1X6ctklZz6NZc,3978
36
- zaturn-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
- zaturn-0.2.2.dist-info/entry_points.txt,sha256=MWMWX0dE_ZQM4StGKCLxJym_D91F82RcBi2LBj0hEho,82
38
- zaturn-0.2.2.dist-info/top_level.txt,sha256=KLUnwQwVZkfd5YCnnqR35MOOs8KLhanPGelvmRo2MVA,7
39
- zaturn-0.2.2.dist-info/RECORD,,
File without changes