syna-agent 0.9.0__tar.gz
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.
- syna_agent-0.9.0/LICENSE +21 -0
- syna_agent-0.9.0/PKG-INFO +132 -0
- syna_agent-0.9.0/README.md +92 -0
- syna_agent-0.9.0/pyproject.toml +67 -0
- syna_agent-0.9.0/pyproject.toml.orig +58 -0
- syna_agent-0.9.0/src/syna/__init__.py +22 -0
- syna_agent-0.9.0/src/syna/ask.py +162 -0
- syna_agent-0.9.0/src/syna/ask_utils.py +215 -0
- syna_agent-0.9.0/src/syna/assets/__init__.py +1 -0
- syna_agent-0.9.0/src/syna/assets/chat.html +4230 -0
- syna_agent-0.9.0/src/syna/assets/compaction_prompt.md +63 -0
- syna_agent-0.9.0/src/syna/assets/config.json +44 -0
- syna_agent-0.9.0/src/syna/assets/prompt.md +140 -0
- syna_agent-0.9.0/src/syna/assets/setup.html +792 -0
- syna_agent-0.9.0/src/syna/assets/vendor/Lora.OFL.txt +93 -0
- syna_agent-0.9.0/src/syna/assets/vendor/auto-render.min.js +1 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_AMS-Regular.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_AMS-Regular.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Bold.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Bold.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Bold.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Italic.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Italic.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Italic.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Regular.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Regular.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Regular.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Math-Italic.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Math-Italic.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Math-Italic.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Script-Regular.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Script-Regular.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Script-Regular.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size1-Regular.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size1-Regular.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size2-Regular.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size2-Regular.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size3-Regular.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size3-Regular.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size4-Regular.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size4-Regular.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/MaterialSymbolsOutlined.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/lora-ext-latin-400-italic.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/lora-ext-latin-400-normal.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/lora-ext-latin-700-normal.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/lora-latin-400-italic.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/lora-latin-400-normal.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/lora-latin-700-normal.woff2 +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/fonts/material-symbols.LICENSE.txt +202 -0
- syna_agent-0.9.0/src/syna/assets/vendor/katex.LICENSE.txt +21 -0
- syna_agent-0.9.0/src/syna/assets/vendor/katex.min.css +1 -0
- syna_agent-0.9.0/src/syna/assets/vendor/katex.min.js +1 -0
- syna_agent-0.9.0/src/syna/assets/vendor/markdown-it.LICENSE.txt +22 -0
- syna_agent-0.9.0/src/syna/assets/vendor/markdown-it.min.js +2 -0
- syna_agent-0.9.0/src/syna/assets/vendor/syna-favicon.png +0 -0
- syna_agent-0.9.0/src/syna/assets/vendor/syna-logo.png +0 -0
- syna_agent-0.9.0/src/syna/assets/verifier_prompt.md +7 -0
- syna_agent-0.9.0/src/syna/browser.py +865 -0
- syna_agent-0.9.0/src/syna/channel.py +118 -0
- syna_agent-0.9.0/src/syna/commands.py +151 -0
- syna_agent-0.9.0/src/syna/compaction.py +104 -0
- syna_agent-0.9.0/src/syna/config.py +128 -0
- syna_agent-0.9.0/src/syna/context.py +139 -0
- syna_agent-0.9.0/src/syna/editor.py +120 -0
- syna_agent-0.9.0/src/syna/image_generation.py +114 -0
- syna_agent-0.9.0/src/syna/imaging.py +36 -0
- syna_agent-0.9.0/src/syna/inspect_module.py +83 -0
- syna_agent-0.9.0/src/syna/interaction.py +90 -0
- syna_agent-0.9.0/src/syna/net_guard.py +231 -0
- syna_agent-0.9.0/src/syna/output.py +54 -0
- syna_agent-0.9.0/src/syna/parse.py +718 -0
- syna_agent-0.9.0/src/syna/paths.py +86 -0
- syna_agent-0.9.0/src/syna/server.py +865 -0
- syna_agent-0.9.0/src/syna/session.py +998 -0
- syna_agent-0.9.0/src/syna/setup.py +348 -0
- syna_agent-0.9.0/src/syna/speech.py +295 -0
- syna_agent-0.9.0/src/syna/state.py +170 -0
- syna_agent-0.9.0/src/syna/tool_schemas.py +125 -0
- syna_agent-0.9.0/src/syna/tools.py +593 -0
- syna_agent-0.9.0/src/syna/user_messages.py +63 -0
- syna_agent-0.9.0/src/syna/verification.py +122 -0
- syna_agent-0.9.0/src/syna/vision.py +94 -0
- syna_agent-0.9.0/src/syna/web_search.py +321 -0
- syna_agent-0.9.0/src/syna/web_to_markdown.py +379 -0
syna_agent-0.9.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Julynx
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: syna-agent
|
|
3
|
+
Version: 0.9.0
|
|
4
|
+
Summary: Terminal and web AI agent that answers requests by driving an LLM in a tool-call loop inside a sandboxed container pod.
|
|
5
|
+
Keywords: ai,agent,llm,tools,sandbox,openrouter,assistant
|
|
6
|
+
Author: Julynx
|
|
7
|
+
Author-email: Julynx <julicabria@outlook.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Environment :: Web Environment
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Requires-Dist: openrouter>=1.1.137
|
|
24
|
+
Requires-Dist: python-dotenv>=1.2.3
|
|
25
|
+
Requires-Dist: httpx>=0.27
|
|
26
|
+
Requires-Dist: bollard>=0.1.3
|
|
27
|
+
Requires-Dist: playwright==1.62.0
|
|
28
|
+
Requires-Dist: drissionpage>=4.1.1.4
|
|
29
|
+
Requires-Dist: markdownify>=1.2.3
|
|
30
|
+
Requires-Dist: pillow>=11.0.0
|
|
31
|
+
Requires-Dist: fastapi>=0.115.0
|
|
32
|
+
Requires-Dist: uvicorn[standard]>=0.30.0
|
|
33
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
34
|
+
Requires-Dist: camoufox[geoip]>=0.5.8
|
|
35
|
+
Requires-Python: >=3.12
|
|
36
|
+
Project-URL: Homepage, https://julynx.github.io/syna/
|
|
37
|
+
Project-URL: Repository, https://github.com/Julynx/syna
|
|
38
|
+
Project-URL: Issues, https://github.com/Julynx/syna/issues
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
<p align="center">
|
|
42
|
+
<img
|
|
43
|
+
src="https://raw.githubusercontent.com/Julynx/syna/main/website/assets/syna-logo-pill.png"
|
|
44
|
+
width="260px"
|
|
45
|
+
/>
|
|
46
|
+
</p>
|
|
47
|
+
|
|
48
|
+
<h3 align="center">Syna</h3>
|
|
49
|
+
<p align="center">
|
|
50
|
+
<i>The AI agent with a pod.</i>
|
|
51
|
+
<br />
|
|
52
|
+
<a href="https://julynx.github.io/syna/" target="_blank">julynx.github.io/syna</a>
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
<hr />
|
|
56
|
+
|
|
57
|
+
<p align="center">
|
|
58
|
+
<h3 align="center"><code>uv tool install syna-agent</code></h3>
|
|
59
|
+
</p>
|
|
60
|
+
|
|
61
|
+

|
|
62
|
+
|
|
63
|
+

|
|
64
|
+
|
|
65
|
+
## Features
|
|
66
|
+
|
|
67
|
+
- **A sandboxed pod.** Every command runs inside an isolated container, with a full Debian shell, root access, and your host machine out of reach.
|
|
68
|
+
- **A complete toolset.** Web search and browser automation (both local, no API key), image understanding, image generation, speech-to-text, text-to-speech, and precise line-number, diff-style file editing.
|
|
69
|
+
- **Built for long, complex tasks.** Every tool output is saved to a file, context is managed automatically (elision and compaction, with a high cache hit rate), and deliverables keep a verifiable chain of custody from source to artifact.
|
|
70
|
+
- **Honest reporting.** Syna is trained to say exactly what happened and what it can't verify; it resists prompt injection and false-flag attacks baked into web content, and it scores at the top of honesty benchmarks.
|
|
71
|
+
- **It asks when it should.** Multiple-choice or open-ended questions, mid-task, whenever a decision isn't Syna's to make.
|
|
72
|
+
- **Voice mode.** Talk to it, and it reads its answers out loud.
|
|
73
|
+
- **A beautiful UI.** Frosted glass, sound effects, and a Y2K-video-player-style background visualizer that reacts to the agent (thinking, calling tools, speaking).
|
|
74
|
+
- **Any provider.** Works with OpenRouter (recommended, most tested), OpenAI, Google Gemini, local Ollama, or any OpenAI-compatible endpoint.
|
|
75
|
+
|
|
76
|
+
## Getting started
|
|
77
|
+
|
|
78
|
+
1. Install:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
uv tool install syna-agent
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
2. Run the setup wizard and follow the steps (dependencies, provider, API key, sanity check):
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
syna-setup
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
3. Start Syna (the terminal version, or the web UI at `http://127.0.0.1:8000`):
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
syna
|
|
94
|
+
syna-server
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The wizard writes a `.env` file in `~/.syna` for you. To configure manually, create `~/.syna/.env` with:
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
OPENROUTER_API_KEY=your-api-key
|
|
101
|
+
MODEL=z-ai/glm-5.3-flash # Or any one of your preference
|
|
102
|
+
BASE_URL=https://openrouter.ai/api/v1 # Optional: any OpenAI-compatible endpoint
|
|
103
|
+
THINKING_EFFORT=medium # Can be either of none|minimal|low|medium|high|xhigh|max
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Voice mode is enabled by adding speech models to the same `.env` file:
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
STT_MODEL=google/chirp-3 # Transcribes microphone recordings
|
|
110
|
+
TTS_MODEL=google/gemini-3.8-flash-lite-tts # Reads the answers out loud
|
|
111
|
+
TTS_VOICE=Kore # Optional: voice of the TTS model
|
|
112
|
+
SPEECH_LANGUAGE=en # Optional: ISO-639-1 code, English by default
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Image generation (the model's `text_to_image` tool) is enabled the same way:
|
|
116
|
+
|
|
117
|
+
```sh
|
|
118
|
+
IMAGE_MODEL=google/gemini-3.1-flash-image
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Requirements: Python 3.12+ and a container engine (Podman or Docker; the setup wizard checks and installs one for you).
|
|
122
|
+
|
|
123
|
+
## Privacy
|
|
124
|
+
|
|
125
|
+
Syna does not collect your personal information or send your messages anywhere except to the provider you specified. Their Terms of Service and Privacy Policy apply.
|
|
126
|
+
|
|
127
|
+
## Links
|
|
128
|
+
|
|
129
|
+
- [Website](https://julynx.github.io/syna/)
|
|
130
|
+
- [PyPI](https://pypi.org/project/syna-agent/)
|
|
131
|
+
- [Technical description](docs/technical_description.md)
|
|
132
|
+
- [Report an issue](https://github.com/Julynx/syna/issues)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img
|
|
3
|
+
src="https://raw.githubusercontent.com/Julynx/syna/main/website/assets/syna-logo-pill.png"
|
|
4
|
+
width="260px"
|
|
5
|
+
/>
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<h3 align="center">Syna</h3>
|
|
9
|
+
<p align="center">
|
|
10
|
+
<i>The AI agent with a pod.</i>
|
|
11
|
+
<br />
|
|
12
|
+
<a href="https://julynx.github.io/syna/" target="_blank">julynx.github.io/syna</a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<hr />
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<h3 align="center"><code>uv tool install syna-agent</code></h3>
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+

|
|
22
|
+
|
|
23
|
+

|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- **A sandboxed pod.** Every command runs inside an isolated container, with a full Debian shell, root access, and your host machine out of reach.
|
|
28
|
+
- **A complete toolset.** Web search and browser automation (both local, no API key), image understanding, image generation, speech-to-text, text-to-speech, and precise line-number, diff-style file editing.
|
|
29
|
+
- **Built for long, complex tasks.** Every tool output is saved to a file, context is managed automatically (elision and compaction, with a high cache hit rate), and deliverables keep a verifiable chain of custody from source to artifact.
|
|
30
|
+
- **Honest reporting.** Syna is trained to say exactly what happened and what it can't verify; it resists prompt injection and false-flag attacks baked into web content, and it scores at the top of honesty benchmarks.
|
|
31
|
+
- **It asks when it should.** Multiple-choice or open-ended questions, mid-task, whenever a decision isn't Syna's to make.
|
|
32
|
+
- **Voice mode.** Talk to it, and it reads its answers out loud.
|
|
33
|
+
- **A beautiful UI.** Frosted glass, sound effects, and a Y2K-video-player-style background visualizer that reacts to the agent (thinking, calling tools, speaking).
|
|
34
|
+
- **Any provider.** Works with OpenRouter (recommended, most tested), OpenAI, Google Gemini, local Ollama, or any OpenAI-compatible endpoint.
|
|
35
|
+
|
|
36
|
+
## Getting started
|
|
37
|
+
|
|
38
|
+
1. Install:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
uv tool install syna-agent
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
2. Run the setup wizard and follow the steps (dependencies, provider, API key, sanity check):
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
syna-setup
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
3. Start Syna (the terminal version, or the web UI at `http://127.0.0.1:8000`):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
syna
|
|
54
|
+
syna-server
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The wizard writes a `.env` file in `~/.syna` for you. To configure manually, create `~/.syna/.env` with:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
OPENROUTER_API_KEY=your-api-key
|
|
61
|
+
MODEL=z-ai/glm-5.3-flash # Or any one of your preference
|
|
62
|
+
BASE_URL=https://openrouter.ai/api/v1 # Optional: any OpenAI-compatible endpoint
|
|
63
|
+
THINKING_EFFORT=medium # Can be either of none|minimal|low|medium|high|xhigh|max
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Voice mode is enabled by adding speech models to the same `.env` file:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
STT_MODEL=google/chirp-3 # Transcribes microphone recordings
|
|
70
|
+
TTS_MODEL=google/gemini-3.8-flash-lite-tts # Reads the answers out loud
|
|
71
|
+
TTS_VOICE=Kore # Optional: voice of the TTS model
|
|
72
|
+
SPEECH_LANGUAGE=en # Optional: ISO-639-1 code, English by default
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Image generation (the model's `text_to_image` tool) is enabled the same way:
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
IMAGE_MODEL=google/gemini-3.1-flash-image
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Requirements: Python 3.12+ and a container engine (Podman or Docker; the setup wizard checks and installs one for you).
|
|
82
|
+
|
|
83
|
+
## Privacy
|
|
84
|
+
|
|
85
|
+
Syna does not collect your personal information or send your messages anywhere except to the provider you specified. Their Terms of Service and Privacy Policy apply.
|
|
86
|
+
|
|
87
|
+
## Links
|
|
88
|
+
|
|
89
|
+
- [Website](https://julynx.github.io/syna/)
|
|
90
|
+
- [PyPI](https://pypi.org/project/syna-agent/)
|
|
91
|
+
- [Technical description](docs/technical_description.md)
|
|
92
|
+
- [Report an issue](https://github.com/Julynx/syna/issues)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "syna-agent"
|
|
3
|
+
version = "0.9.0"
|
|
4
|
+
description = "Terminal and web AI agent that answers requests by driving an LLM in a tool-call loop inside a sandboxed container pod."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
requires-python = ">=3.12"
|
|
9
|
+
keywords = [
|
|
10
|
+
"ai",
|
|
11
|
+
"agent",
|
|
12
|
+
"llm",
|
|
13
|
+
"tools",
|
|
14
|
+
"sandbox",
|
|
15
|
+
"openrouter",
|
|
16
|
+
"assistant",
|
|
17
|
+
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 4 - Beta",
|
|
20
|
+
"Environment :: Console",
|
|
21
|
+
"Environment :: Web Environment",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"Intended Audience :: End Users/Desktop",
|
|
24
|
+
"Operating System :: Microsoft :: Windows",
|
|
25
|
+
"Operating System :: POSIX :: Linux",
|
|
26
|
+
"Operating System :: MacOS",
|
|
27
|
+
"Programming Language :: Python :: 3",
|
|
28
|
+
"Programming Language :: Python :: 3.12",
|
|
29
|
+
"Programming Language :: Python :: 3.13",
|
|
30
|
+
"Programming Language :: Python :: 3.14",
|
|
31
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
32
|
+
]
|
|
33
|
+
dependencies = [
|
|
34
|
+
"openrouter>=1.1.137",
|
|
35
|
+
"python-dotenv>=1.2.3",
|
|
36
|
+
"httpx>=0.27",
|
|
37
|
+
"bollard>=0.1.3",
|
|
38
|
+
"playwright==1.62.0",
|
|
39
|
+
"drissionpage>=4.1.1.4",
|
|
40
|
+
"markdownify>=1.2.3",
|
|
41
|
+
"pillow>=11.0.0",
|
|
42
|
+
"fastapi>=0.115.0",
|
|
43
|
+
"uvicorn[standard]>=0.30.0",
|
|
44
|
+
"python-multipart>=0.0.9",
|
|
45
|
+
"camoufox[geoip]>=0.5.8",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[[project.authors]]
|
|
49
|
+
name = "Julynx"
|
|
50
|
+
email = "julicabria@outlook.com"
|
|
51
|
+
|
|
52
|
+
[project.scripts]
|
|
53
|
+
syna = "syna:main"
|
|
54
|
+
syna-server = "syna.server:main"
|
|
55
|
+
syna-setup = "syna.setup:main"
|
|
56
|
+
|
|
57
|
+
[project.urls]
|
|
58
|
+
Homepage = "https://julynx.github.io/syna/"
|
|
59
|
+
Repository = "https://github.com/Julynx/syna"
|
|
60
|
+
Issues = "https://github.com/Julynx/syna/issues"
|
|
61
|
+
|
|
62
|
+
[build-system]
|
|
63
|
+
requires = ["uv_build>=0.12.3,<0.13.0"]
|
|
64
|
+
build-backend = "uv_build"
|
|
65
|
+
|
|
66
|
+
[tool.uv.build-backend]
|
|
67
|
+
module-name = "syna"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "syna-agent"
|
|
3
|
+
version = "0.9.0"
|
|
4
|
+
description = "Terminal and web AI agent that answers requests by driving an LLM in a tool-call loop inside a sandboxed container pod."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Julynx", email = "julicabria@outlook.com" }
|
|
10
|
+
]
|
|
11
|
+
requires-python = ">=3.12"
|
|
12
|
+
keywords = ["ai", "agent", "llm", "tools", "sandbox", "openrouter", "assistant"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Environment :: Console",
|
|
16
|
+
"Environment :: Web Environment",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Intended Audience :: End Users/Desktop",
|
|
19
|
+
"Operating System :: Microsoft :: Windows",
|
|
20
|
+
"Operating System :: POSIX :: Linux",
|
|
21
|
+
"Operating System :: MacOS",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Programming Language :: Python :: 3.14",
|
|
26
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
27
|
+
]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"openrouter>=1.1.137",
|
|
30
|
+
"python-dotenv>=1.2.3",
|
|
31
|
+
"httpx>=0.27",
|
|
32
|
+
"bollard>=0.1.3",
|
|
33
|
+
"playwright==1.62.0",
|
|
34
|
+
"drissionpage>=4.1.1.4",
|
|
35
|
+
"markdownify>=1.2.3",
|
|
36
|
+
"pillow>=11.0.0",
|
|
37
|
+
"fastapi>=0.115.0",
|
|
38
|
+
"uvicorn[standard]>=0.30.0",
|
|
39
|
+
"python-multipart>=0.0.9",
|
|
40
|
+
"camoufox[geoip]>=0.5.8",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[project.scripts]
|
|
44
|
+
syna = "syna:main"
|
|
45
|
+
syna-server = "syna.server:main"
|
|
46
|
+
syna-setup = "syna.setup:main"
|
|
47
|
+
|
|
48
|
+
[project.urls]
|
|
49
|
+
Homepage = "https://julynx.github.io/syna/"
|
|
50
|
+
Repository = "https://github.com/Julynx/syna"
|
|
51
|
+
Issues = "https://github.com/Julynx/syna/issues"
|
|
52
|
+
|
|
53
|
+
[build-system]
|
|
54
|
+
requires = ["uv_build>=0.12.3,<0.13.0"]
|
|
55
|
+
build-backend = "uv_build"
|
|
56
|
+
|
|
57
|
+
[tool.uv.build-backend]
|
|
58
|
+
module-name = "syna"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Application entrypoint for Syna AI agent."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from .ask import ask_loop
|
|
6
|
+
from .state import close_docker_client
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main() -> None:
|
|
10
|
+
"""Start the interactive agent loop."""
|
|
11
|
+
try:
|
|
12
|
+
ask_loop()
|
|
13
|
+
except KeyboardInterrupt:
|
|
14
|
+
print("\n + (Received CTRL+C)")
|
|
15
|
+
sys.exit(0)
|
|
16
|
+
finally:
|
|
17
|
+
try:
|
|
18
|
+
print(" + (Cleaning up container and shutting down, please wait...)")
|
|
19
|
+
close_docker_client()
|
|
20
|
+
print(" + (Cleanup finished. Goodbye!)\n")
|
|
21
|
+
except KeyboardInterrupt:
|
|
22
|
+
pass
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""CLI interaction loop built on top of the shared AgentSession."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from .ask_utils import show_welcome
|
|
6
|
+
from .commands import parse_and_execute_command
|
|
7
|
+
from .interaction import InteractionCancelled, register_responder
|
|
8
|
+
from .interaction import unregister_responder
|
|
9
|
+
from .session import AgentSession
|
|
10
|
+
from .state import reset_shared_container
|
|
11
|
+
|
|
12
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
13
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
14
|
+
if hasattr(sys.stderr, "reconfigure"):
|
|
15
|
+
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
|
16
|
+
|
|
17
|
+
NEW_CHAT_COMMAND = "/newchat"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _print_status_line(text: str):
|
|
21
|
+
"""Overwrite the progress line so repeated statuses don't scroll."""
|
|
22
|
+
print(f" + ({text})", end="\r", flush=True)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _print_tool_call(event):
|
|
26
|
+
"""Render one tool_call event: started, finished, failed, or skipped."""
|
|
27
|
+
status = event.get("status")
|
|
28
|
+
signature = str(event.get("signature", "")).strip()[:64].replace("\n", " ")
|
|
29
|
+
if status == "started":
|
|
30
|
+
_print_status_line(f"Running {signature}...")
|
|
31
|
+
return
|
|
32
|
+
if status == "skipped":
|
|
33
|
+
print(f" + │Skipped '{signature}...' (an earlier call in the batch failed)")
|
|
34
|
+
_print_status_line("Thinking...")
|
|
35
|
+
return
|
|
36
|
+
if status != "finished":
|
|
37
|
+
return
|
|
38
|
+
shared = event.get("structured_output")
|
|
39
|
+
if isinstance(shared, dict) and "path" in shared:
|
|
40
|
+
print(f" + │Shared '{shared.get('name', '')}'")
|
|
41
|
+
print(f" └─ Save it locally with: /get-from {shared['path']}")
|
|
42
|
+
_print_status_line("Thinking...")
|
|
43
|
+
return
|
|
44
|
+
output = str(event.get("output_preview", "")).strip()[:64]
|
|
45
|
+
if event.get("failed"):
|
|
46
|
+
print(f" + ✗Failed '{signature}...'")
|
|
47
|
+
else:
|
|
48
|
+
print(f" + │Used tool '{signature}...'")
|
|
49
|
+
print(f" └─ {output}...")
|
|
50
|
+
_print_status_line("Thinking...")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _print_event(event):
|
|
54
|
+
"""Render session events with the classic CLI look and feel."""
|
|
55
|
+
kind = event["type"]
|
|
56
|
+
if kind == "status":
|
|
57
|
+
_print_status_line(event["text"])
|
|
58
|
+
elif kind == "tool_call":
|
|
59
|
+
_print_tool_call(event)
|
|
60
|
+
elif kind == "model_message":
|
|
61
|
+
print(f"\n[Syna]: {event['content']}\n")
|
|
62
|
+
elif kind == "compaction":
|
|
63
|
+
print(f"\n----- Compaction -----\n{event['summary']}\n")
|
|
64
|
+
elif kind == "error":
|
|
65
|
+
print(f"\n ! ({event['text']})")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _terminal_responder(session: AgentSession):
|
|
69
|
+
"""Build an ask_user responder that reads answers from the terminal.
|
|
70
|
+
|
|
71
|
+
A number picks one of the shown choices; any other text is delivered
|
|
72
|
+
as a custom answer. Ctrl+C (or a closed stdin) cancels the pending
|
|
73
|
+
question and stops the task instead of killing the application.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
def responder(question_text: str, choices: list[str]) -> str:
|
|
77
|
+
print(f"\n ? {question_text}")
|
|
78
|
+
if choices:
|
|
79
|
+
for number, choice in enumerate(choices, start=1):
|
|
80
|
+
print(f" {number}. {choice}")
|
|
81
|
+
print(" Answer with a number, or type your own response.")
|
|
82
|
+
_print_status_line("Waiting for your answer...")
|
|
83
|
+
try:
|
|
84
|
+
while True:
|
|
85
|
+
answer = input(" > ").strip()
|
|
86
|
+
if not answer:
|
|
87
|
+
continue
|
|
88
|
+
if choices and answer.isdigit() and 1 <= int(answer) <= len(choices):
|
|
89
|
+
return choices[int(answer) - 1]
|
|
90
|
+
return answer
|
|
91
|
+
except (KeyboardInterrupt, EOFError):
|
|
92
|
+
session.cancel()
|
|
93
|
+
raise InteractionCancelled(
|
|
94
|
+
"The user stopped the task before answering."
|
|
95
|
+
) from None
|
|
96
|
+
|
|
97
|
+
return responder
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _handle_slash_command(query: str, session: AgentSession):
|
|
101
|
+
"""Execute a slash command and echo its result or error."""
|
|
102
|
+
try:
|
|
103
|
+
result = parse_and_execute_command(query, session=session)
|
|
104
|
+
print(f" + ({result})")
|
|
105
|
+
except ValueError as exc:
|
|
106
|
+
print(f" ! ({exc})")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _create_session() -> AgentSession:
|
|
110
|
+
"""Create an agent session that renders its events on the terminal."""
|
|
111
|
+
return AgentSession(on_event=_print_event, persist_outputs=True)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _reset_session(session: AgentSession) -> AgentSession:
|
|
115
|
+
"""Drop the conversation and destroy the shared pod (/newchat)."""
|
|
116
|
+
print(" + (Cleaning up the pod, please wait...)")
|
|
117
|
+
try:
|
|
118
|
+
session.close()
|
|
119
|
+
except Exception:
|
|
120
|
+
pass
|
|
121
|
+
try:
|
|
122
|
+
reset_shared_container()
|
|
123
|
+
except Exception:
|
|
124
|
+
print(" ! (Could not clean up the old pod; starting a new session anyway.)")
|
|
125
|
+
print(" + (Started a fresh chat.)")
|
|
126
|
+
return _create_session()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def ask_loop():
|
|
130
|
+
"""Execute the interactive conversation and tool execution loop."""
|
|
131
|
+
show_welcome()
|
|
132
|
+
session = _create_session()
|
|
133
|
+
register_responder(_terminal_responder(session))
|
|
134
|
+
try:
|
|
135
|
+
while True:
|
|
136
|
+
try:
|
|
137
|
+
query = input("(Ask Syna): ")
|
|
138
|
+
except EOFError:
|
|
139
|
+
break
|
|
140
|
+
if not query.strip():
|
|
141
|
+
continue
|
|
142
|
+
if query.strip() == NEW_CHAT_COMMAND:
|
|
143
|
+
session = _reset_session(session)
|
|
144
|
+
register_responder(_terminal_responder(session))
|
|
145
|
+
continue
|
|
146
|
+
if query.strip().startswith("/"):
|
|
147
|
+
_handle_slash_command(query, session)
|
|
148
|
+
continue
|
|
149
|
+
session.clear_cancel()
|
|
150
|
+
try:
|
|
151
|
+
session.run_turn(query)
|
|
152
|
+
except KeyboardInterrupt:
|
|
153
|
+
session.cancel()
|
|
154
|
+
print("\n + (Task stopped. Press CTRL+C again to quit.)")
|
|
155
|
+
except InteractionCancelled:
|
|
156
|
+
print("\n + (Task stopped.)")
|
|
157
|
+
finally:
|
|
158
|
+
unregister_responder()
|
|
159
|
+
try:
|
|
160
|
+
session.close()
|
|
161
|
+
except Exception:
|
|
162
|
+
pass
|