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.
Files changed (119) hide show
  1. syna_agent-0.9.0/LICENSE +21 -0
  2. syna_agent-0.9.0/PKG-INFO +132 -0
  3. syna_agent-0.9.0/README.md +92 -0
  4. syna_agent-0.9.0/pyproject.toml +67 -0
  5. syna_agent-0.9.0/pyproject.toml.orig +58 -0
  6. syna_agent-0.9.0/src/syna/__init__.py +22 -0
  7. syna_agent-0.9.0/src/syna/ask.py +162 -0
  8. syna_agent-0.9.0/src/syna/ask_utils.py +215 -0
  9. syna_agent-0.9.0/src/syna/assets/__init__.py +1 -0
  10. syna_agent-0.9.0/src/syna/assets/chat.html +4230 -0
  11. syna_agent-0.9.0/src/syna/assets/compaction_prompt.md +63 -0
  12. syna_agent-0.9.0/src/syna/assets/config.json +44 -0
  13. syna_agent-0.9.0/src/syna/assets/prompt.md +140 -0
  14. syna_agent-0.9.0/src/syna/assets/setup.html +792 -0
  15. syna_agent-0.9.0/src/syna/assets/vendor/Lora.OFL.txt +93 -0
  16. syna_agent-0.9.0/src/syna/assets/vendor/auto-render.min.js +1 -0
  17. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_AMS-Regular.ttf +0 -0
  18. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_AMS-Regular.woff +0 -0
  19. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  20. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  21. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  22. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  23. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  24. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  25. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  26. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  27. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  28. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  29. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  30. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  31. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  32. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Bold.ttf +0 -0
  33. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Bold.woff +0 -0
  34. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Bold.woff2 +0 -0
  35. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  36. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  37. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  38. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Italic.ttf +0 -0
  39. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Italic.woff +0 -0
  40. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Italic.woff2 +0 -0
  41. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Regular.ttf +0 -0
  42. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Regular.woff +0 -0
  43. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Main-Regular.woff2 +0 -0
  44. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  45. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  46. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  47. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Math-Italic.ttf +0 -0
  48. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Math-Italic.woff +0 -0
  49. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Math-Italic.woff2 +0 -0
  50. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  51. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  52. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  53. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  54. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  55. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  56. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  57. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  58. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  59. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Script-Regular.ttf +0 -0
  60. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Script-Regular.woff +0 -0
  61. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Script-Regular.woff2 +0 -0
  62. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size1-Regular.ttf +0 -0
  63. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size1-Regular.woff +0 -0
  64. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  65. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size2-Regular.ttf +0 -0
  66. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size2-Regular.woff +0 -0
  67. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  68. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size3-Regular.ttf +0 -0
  69. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size3-Regular.woff +0 -0
  70. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  71. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size4-Regular.ttf +0 -0
  72. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size4-Regular.woff +0 -0
  73. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  74. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  75. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  76. syna_agent-0.9.0/src/syna/assets/vendor/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  77. syna_agent-0.9.0/src/syna/assets/vendor/fonts/MaterialSymbolsOutlined.woff2 +0 -0
  78. syna_agent-0.9.0/src/syna/assets/vendor/fonts/lora-ext-latin-400-italic.woff2 +0 -0
  79. syna_agent-0.9.0/src/syna/assets/vendor/fonts/lora-ext-latin-400-normal.woff2 +0 -0
  80. syna_agent-0.9.0/src/syna/assets/vendor/fonts/lora-ext-latin-700-normal.woff2 +0 -0
  81. syna_agent-0.9.0/src/syna/assets/vendor/fonts/lora-latin-400-italic.woff2 +0 -0
  82. syna_agent-0.9.0/src/syna/assets/vendor/fonts/lora-latin-400-normal.woff2 +0 -0
  83. syna_agent-0.9.0/src/syna/assets/vendor/fonts/lora-latin-700-normal.woff2 +0 -0
  84. syna_agent-0.9.0/src/syna/assets/vendor/fonts/material-symbols.LICENSE.txt +202 -0
  85. syna_agent-0.9.0/src/syna/assets/vendor/katex.LICENSE.txt +21 -0
  86. syna_agent-0.9.0/src/syna/assets/vendor/katex.min.css +1 -0
  87. syna_agent-0.9.0/src/syna/assets/vendor/katex.min.js +1 -0
  88. syna_agent-0.9.0/src/syna/assets/vendor/markdown-it.LICENSE.txt +22 -0
  89. syna_agent-0.9.0/src/syna/assets/vendor/markdown-it.min.js +2 -0
  90. syna_agent-0.9.0/src/syna/assets/vendor/syna-favicon.png +0 -0
  91. syna_agent-0.9.0/src/syna/assets/vendor/syna-logo.png +0 -0
  92. syna_agent-0.9.0/src/syna/assets/verifier_prompt.md +7 -0
  93. syna_agent-0.9.0/src/syna/browser.py +865 -0
  94. syna_agent-0.9.0/src/syna/channel.py +118 -0
  95. syna_agent-0.9.0/src/syna/commands.py +151 -0
  96. syna_agent-0.9.0/src/syna/compaction.py +104 -0
  97. syna_agent-0.9.0/src/syna/config.py +128 -0
  98. syna_agent-0.9.0/src/syna/context.py +139 -0
  99. syna_agent-0.9.0/src/syna/editor.py +120 -0
  100. syna_agent-0.9.0/src/syna/image_generation.py +114 -0
  101. syna_agent-0.9.0/src/syna/imaging.py +36 -0
  102. syna_agent-0.9.0/src/syna/inspect_module.py +83 -0
  103. syna_agent-0.9.0/src/syna/interaction.py +90 -0
  104. syna_agent-0.9.0/src/syna/net_guard.py +231 -0
  105. syna_agent-0.9.0/src/syna/output.py +54 -0
  106. syna_agent-0.9.0/src/syna/parse.py +718 -0
  107. syna_agent-0.9.0/src/syna/paths.py +86 -0
  108. syna_agent-0.9.0/src/syna/server.py +865 -0
  109. syna_agent-0.9.0/src/syna/session.py +998 -0
  110. syna_agent-0.9.0/src/syna/setup.py +348 -0
  111. syna_agent-0.9.0/src/syna/speech.py +295 -0
  112. syna_agent-0.9.0/src/syna/state.py +170 -0
  113. syna_agent-0.9.0/src/syna/tool_schemas.py +125 -0
  114. syna_agent-0.9.0/src/syna/tools.py +593 -0
  115. syna_agent-0.9.0/src/syna/user_messages.py +63 -0
  116. syna_agent-0.9.0/src/syna/verification.py +122 -0
  117. syna_agent-0.9.0/src/syna/vision.py +94 -0
  118. syna_agent-0.9.0/src/syna/web_search.py +321 -0
  119. syna_agent-0.9.0/src/syna/web_to_markdown.py +379 -0
@@ -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
+ ![Syna chat interface](website/assets/screenshot.png)
62
+
63
+ ![Syna voice mode with the ambient visualizer](website/assets/screenshot2.png)
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
+ ![Syna chat interface](website/assets/screenshot.png)
22
+
23
+ ![Syna voice mode with the ambient visualizer](website/assets/screenshot2.png)
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