tarmina 0.1.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.
- tarmina-0.1.0/LICENSE +21 -0
- tarmina-0.1.0/PKG-INFO +65 -0
- tarmina-0.1.0/README.md +32 -0
- tarmina-0.1.0/pyproject.toml +50 -0
- tarmina-0.1.0/setup.cfg +4 -0
- tarmina-0.1.0/setup.py +27 -0
- tarmina-0.1.0/tarmina/__init__.py +13 -0
- tarmina-0.1.0/tarmina/ai.py +157 -0
- tarmina-0.1.0/tarmina/app.py +372 -0
- tarmina-0.1.0/tarmina/cli.py +265 -0
- tarmina-0.1.0/tarmina/config.py +79 -0
- tarmina-0.1.0/tarmina/config.yaml +31 -0
- tarmina-0.1.0/tarmina/memory.py +101 -0
- tarmina-0.1.0/tarmina/tarmina.css +127 -0
- tarmina-0.1.0/tarmina/tools/__init__.py +0 -0
- tarmina-0.1.0/tarmina/tools/system.py +156 -0
- tarmina-0.1.0/tarmina.egg-info/PKG-INFO +65 -0
- tarmina-0.1.0/tarmina.egg-info/SOURCES.txt +20 -0
- tarmina-0.1.0/tarmina.egg-info/dependency_links.txt +1 -0
- tarmina-0.1.0/tarmina.egg-info/entry_points.txt +2 -0
- tarmina-0.1.0/tarmina.egg-info/requires.txt +9 -0
- tarmina-0.1.0/tarmina.egg-info/top_level.txt +1 -0
tarmina-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tarmina
|
|
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.
|
tarmina-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tarmina
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Personal AI Assistant CLI with TUI
|
|
5
|
+
Author-email: Tarmina <tarmina@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yourusername/tarmina
|
|
8
|
+
Project-URL: Repository, https://github.com/yourusername/tarmina
|
|
9
|
+
Project-URL: Documentation, https://github.com/yourusername/tarmina
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: rich>=13.0.0
|
|
23
|
+
Requires-Dist: prompt_toolkit>=3.0.0
|
|
24
|
+
Requires-Dist: pyyaml>=6.0
|
|
25
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
26
|
+
Requires-Dist: openai>=1.30.0
|
|
27
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
28
|
+
Requires-Dist: httpx>=0.27.0
|
|
29
|
+
Requires-Dist: pyfiglet>=1.0.0
|
|
30
|
+
Requires-Dist: mss>=9.0.0
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
Dynamic: requires-python
|
|
33
|
+
|
|
34
|
+
# Tarmina
|
|
35
|
+
|
|
36
|
+
Personal AI Assistant CLI with TUI (Text User Interface).
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install tarmina
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# First time setup
|
|
48
|
+
tarmina --setup
|
|
49
|
+
|
|
50
|
+
# Start chatting
|
|
51
|
+
tarmina
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Features
|
|
55
|
+
|
|
56
|
+
- Multi-provider AI (OpenAI, Ollama, Groq, DeepSeek, OpenRouter, Custom)
|
|
57
|
+
- TUI chat interface with keyboard shortcuts
|
|
58
|
+
- System tools: execute commands, open apps, read files, list directories, take screenshots
|
|
59
|
+
- Session-based chat history
|
|
60
|
+
- System access control (Full / Folder only / None)
|
|
61
|
+
- Indonesian language personality
|
|
62
|
+
|
|
63
|
+
## Requirements
|
|
64
|
+
|
|
65
|
+
- Python 3.10+
|
tarmina-0.1.0/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Tarmina
|
|
2
|
+
|
|
3
|
+
Personal AI Assistant CLI with TUI (Text User Interface).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install tarmina
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# First time setup
|
|
15
|
+
tarmina --setup
|
|
16
|
+
|
|
17
|
+
# Start chatting
|
|
18
|
+
tarmina
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
- Multi-provider AI (OpenAI, Ollama, Groq, DeepSeek, OpenRouter, Custom)
|
|
24
|
+
- TUI chat interface with keyboard shortcuts
|
|
25
|
+
- System tools: execute commands, open apps, read files, list directories, take screenshots
|
|
26
|
+
- Session-based chat history
|
|
27
|
+
- System access control (Full / Folder only / None)
|
|
28
|
+
- Indonesian language personality
|
|
29
|
+
|
|
30
|
+
## Requirements
|
|
31
|
+
|
|
32
|
+
- Python 3.10+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tarmina"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Personal AI Assistant CLI with TUI"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Tarmina", email = "tarmina@example.com"},
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Environment :: Console",
|
|
18
|
+
"Intended Audience :: End Users/Desktop",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"rich>=13.0.0",
|
|
28
|
+
"prompt_toolkit>=3.0.0",
|
|
29
|
+
"pyyaml>=6.0",
|
|
30
|
+
"python-dotenv>=1.0.0",
|
|
31
|
+
"openai>=1.30.0",
|
|
32
|
+
"aiohttp>=3.9.0",
|
|
33
|
+
"httpx>=0.27.0",
|
|
34
|
+
"pyfiglet>=1.0.0",
|
|
35
|
+
"mss>=9.0.0",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/yourusername/tarmina"
|
|
40
|
+
Repository = "https://github.com/yourusername/tarmina"
|
|
41
|
+
Documentation = "https://github.com/yourusername/tarmina"
|
|
42
|
+
|
|
43
|
+
[project.scripts]
|
|
44
|
+
tarmina = "tarmina.cli:main"
|
|
45
|
+
|
|
46
|
+
[tool.setuptools.packages.find]
|
|
47
|
+
include = ["tarmina*"]
|
|
48
|
+
|
|
49
|
+
[tool.setuptools.package-data]
|
|
50
|
+
tarmina = ["config.yaml"]
|
tarmina-0.1.0/setup.cfg
ADDED
tarmina-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="tarmina",
|
|
5
|
+
version="0.1.0",
|
|
6
|
+
description="Tarmina - Personal AI Assistant CLI",
|
|
7
|
+
packages=find_packages(),
|
|
8
|
+
include_package_data=True,
|
|
9
|
+
package_data={"tarmina": ["config.yaml"]},
|
|
10
|
+
install_requires=[
|
|
11
|
+
"rich>=13.0.0",
|
|
12
|
+
"prompt_toolkit>=3.0.0",
|
|
13
|
+
"pyyaml>=6.0",
|
|
14
|
+
"python-dotenv>=1.0.0",
|
|
15
|
+
"openai>=1.30.0",
|
|
16
|
+
"aiohttp>=3.9.0",
|
|
17
|
+
"httpx>=0.27.0",
|
|
18
|
+
"pyfiglet>=1.0.0",
|
|
19
|
+
"mss>=9.0.0",
|
|
20
|
+
],
|
|
21
|
+
entry_points={
|
|
22
|
+
"console_scripts": [
|
|
23
|
+
"tarmina=tarmina.cli:main",
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
python_requires=">=3.10",
|
|
27
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import io
|
|
3
|
+
|
|
4
|
+
if sys.platform == "win32":
|
|
5
|
+
try:
|
|
6
|
+
sys.stdout = io.TextIOWrapper(
|
|
7
|
+
sys.stdout.buffer, encoding="utf-8", errors="replace", line_buffering=True
|
|
8
|
+
)
|
|
9
|
+
sys.stderr = io.TextIOWrapper(
|
|
10
|
+
sys.stderr.buffer, encoding="utf-8", errors="replace", line_buffering=True
|
|
11
|
+
)
|
|
12
|
+
except Exception:
|
|
13
|
+
pass
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
from typing import AsyncGenerator
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class AIConfig:
|
|
7
|
+
provider: str
|
|
8
|
+
model: str
|
|
9
|
+
api_key: str = ""
|
|
10
|
+
base_url: str = ""
|
|
11
|
+
temperature: float = 0.8
|
|
12
|
+
max_tokens: int = 2048
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AIProvider:
|
|
16
|
+
def __init__(self, config: AIConfig):
|
|
17
|
+
self.config = config
|
|
18
|
+
|
|
19
|
+
async def chat(self, messages: list[dict], system_prompt: str = "") -> str:
|
|
20
|
+
raise NotImplementedError
|
|
21
|
+
|
|
22
|
+
async def stream(self, messages: list[dict], system_prompt: str = "") -> AsyncGenerator[str, None]:
|
|
23
|
+
raise NotImplementedError
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class OpenAIProvider(AIProvider):
|
|
27
|
+
def __init__(self, config: AIConfig):
|
|
28
|
+
super().__init__(config)
|
|
29
|
+
from openai import AsyncOpenAI
|
|
30
|
+
kwargs = {"api_key": config.api_key, "max_retries": 2}
|
|
31
|
+
if config.base_url:
|
|
32
|
+
kwargs["base_url"] = config.base_url
|
|
33
|
+
self.client = AsyncOpenAI(**kwargs)
|
|
34
|
+
|
|
35
|
+
def _build(self, messages, system_prompt):
|
|
36
|
+
msgs = []
|
|
37
|
+
if system_prompt:
|
|
38
|
+
msgs.append({"role": "system", "content": system_prompt})
|
|
39
|
+
msgs.extend(messages)
|
|
40
|
+
return msgs
|
|
41
|
+
|
|
42
|
+
async def chat(self, messages, system_prompt=""):
|
|
43
|
+
r = await self.client.chat.completions.create(
|
|
44
|
+
model=self.config.model,
|
|
45
|
+
messages=self._build(messages, system_prompt),
|
|
46
|
+
temperature=self.config.temperature,
|
|
47
|
+
max_tokens=self.config.max_tokens,
|
|
48
|
+
)
|
|
49
|
+
return r.choices[0].message.content or ""
|
|
50
|
+
|
|
51
|
+
async def stream(self, messages, system_prompt=""):
|
|
52
|
+
s = await self.client.chat.completions.create(
|
|
53
|
+
model=self.config.model,
|
|
54
|
+
messages=self._build(messages, system_prompt),
|
|
55
|
+
temperature=self.config.temperature,
|
|
56
|
+
max_tokens=self.config.max_tokens,
|
|
57
|
+
stream=True,
|
|
58
|
+
)
|
|
59
|
+
async for chunk in s:
|
|
60
|
+
if chunk.choices[0].delta.content:
|
|
61
|
+
yield chunk.choices[0].delta.content
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class OllamaProvider(AIProvider):
|
|
65
|
+
def __init__(self, config: AIConfig):
|
|
66
|
+
super().__init__(config)
|
|
67
|
+
self.base = config.base_url.rstrip("/") if config.base_url else "http://localhost:11434"
|
|
68
|
+
|
|
69
|
+
def _build(self, messages, system_prompt):
|
|
70
|
+
msgs = []
|
|
71
|
+
if system_prompt:
|
|
72
|
+
msgs.append({"role": "system", "content": system_prompt})
|
|
73
|
+
msgs.extend(messages)
|
|
74
|
+
return msgs
|
|
75
|
+
|
|
76
|
+
async def chat(self, messages, system_prompt=""):
|
|
77
|
+
import aiohttp
|
|
78
|
+
import json
|
|
79
|
+
async with aiohttp.ClientSession() as s:
|
|
80
|
+
async with s.post(f"{self.base}/api/chat", json={
|
|
81
|
+
"model": self.config.model, "messages": self._build(messages, system_prompt),
|
|
82
|
+
"stream": False, "options": {"temperature": self.config.temperature},
|
|
83
|
+
}) as r:
|
|
84
|
+
data = await r.json()
|
|
85
|
+
return data.get("message", {}).get("content", "")
|
|
86
|
+
|
|
87
|
+
async def stream(self, messages, system_prompt=""):
|
|
88
|
+
import aiohttp
|
|
89
|
+
import json
|
|
90
|
+
async with aiohttp.ClientSession() as s:
|
|
91
|
+
async with s.post(f"{self.base}/api/chat", json={
|
|
92
|
+
"model": self.config.model, "messages": self._build(messages, system_prompt),
|
|
93
|
+
"stream": True, "options": {"temperature": self.config.temperature},
|
|
94
|
+
}) as r:
|
|
95
|
+
buf = ""
|
|
96
|
+
async for line in r.content:
|
|
97
|
+
line = line.decode().strip()
|
|
98
|
+
if not line:
|
|
99
|
+
continue
|
|
100
|
+
try:
|
|
101
|
+
chunk = json.loads(line)
|
|
102
|
+
content = chunk.get("message", {}).get("content", "")
|
|
103
|
+
if content:
|
|
104
|
+
yield content
|
|
105
|
+
except Exception:
|
|
106
|
+
continue
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class GroqProvider(AIProvider):
|
|
110
|
+
def __init__(self, config: AIConfig):
|
|
111
|
+
super().__init__(config)
|
|
112
|
+
from groq import AsyncGroq
|
|
113
|
+
self.client = AsyncGroq(api_key=config.api_key)
|
|
114
|
+
|
|
115
|
+
def _build(self, messages, system_prompt):
|
|
116
|
+
msgs = []
|
|
117
|
+
if system_prompt:
|
|
118
|
+
msgs.append({"role": "system", "content": system_prompt})
|
|
119
|
+
msgs.extend(messages)
|
|
120
|
+
return msgs
|
|
121
|
+
|
|
122
|
+
async def chat(self, messages, system_prompt=""):
|
|
123
|
+
r = await self.client.chat.completions.create(
|
|
124
|
+
model=self.config.model,
|
|
125
|
+
messages=self._build(messages, system_prompt),
|
|
126
|
+
temperature=self.config.temperature,
|
|
127
|
+
max_tokens=self.config.max_tokens,
|
|
128
|
+
)
|
|
129
|
+
return r.choices[0].message.content or ""
|
|
130
|
+
|
|
131
|
+
async def stream(self, messages, system_prompt=""):
|
|
132
|
+
s = await self.client.chat.completions.create(
|
|
133
|
+
model=self.config.model,
|
|
134
|
+
messages=self._build(messages, system_prompt),
|
|
135
|
+
temperature=self.config.temperature,
|
|
136
|
+
max_tokens=self.config.max_tokens,
|
|
137
|
+
stream=True,
|
|
138
|
+
)
|
|
139
|
+
async for chunk in s:
|
|
140
|
+
if chunk.choices[0].delta.content:
|
|
141
|
+
yield chunk.choices[0].delta.content
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
PROVIDERS = {
|
|
145
|
+
"openai": OpenAIProvider,
|
|
146
|
+
"openrouter": OpenAIProvider,
|
|
147
|
+
"ollama": OllamaProvider,
|
|
148
|
+
"groq": GroqProvider,
|
|
149
|
+
"custom": OpenAIProvider,
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def create_provider(config: AIConfig) -> AIProvider:
|
|
154
|
+
cls = PROVIDERS.get(config.provider)
|
|
155
|
+
if not cls:
|
|
156
|
+
raise ValueError(f"Unknown provider: {config.provider}. Options: {list(PROVIDERS.keys())}")
|
|
157
|
+
return cls(config)
|