termchat-ai 0.1.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.
aiterminal/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ """AITerminal command-line client."""
2
+
3
+ __version__ = "0.1.0"
4
+
aiterminal/client.py ADDED
@@ -0,0 +1,81 @@
1
+ """HTTP client for the AITerminal Netlify Functions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from urllib.parse import urlparse
7
+
8
+ import httpx
9
+
10
+ DEFAULT_API_URL = "https://aiterminal-cloud.netlify.app"
11
+
12
+
13
+ class AITerminalError(Exception):
14
+ """Base exception for user-safe service errors."""
15
+
16
+
17
+ class ServiceError(AITerminalError):
18
+ """The backend returned a safe, user-facing error."""
19
+
20
+
21
+ class AITerminalClient:
22
+ def __init__(
23
+ self,
24
+ base_url: str | None = None,
25
+ *,
26
+ transport: httpx.BaseTransport | None = None,
27
+ timeout: float = 35.0,
28
+ ) -> None:
29
+ configured_url = base_url or os.getenv("AIT_TERMINAL_API_URL") or DEFAULT_API_URL
30
+ self.base_url = configured_url.rstrip("/")
31
+ parsed = urlparse(self.base_url)
32
+ if parsed.scheme != "https" and parsed.hostname not in {"localhost", "127.0.0.1", "::1"}:
33
+ raise ValueError("AIT_TERMINAL_API_URL must use HTTPS (HTTP is allowed only for localhost).")
34
+ if not parsed.netloc:
35
+ raise ValueError("AIT_TERMINAL_API_URL must be an absolute URL.")
36
+ self._http = httpx.Client(base_url=self.base_url, timeout=timeout, transport=transport)
37
+
38
+ def ask(self, message: str, history: list[dict[str, str]]) -> str:
39
+ try:
40
+ result = self._http.post(
41
+ "/.netlify/functions/chat",
42
+ json={"message": message, "history": history},
43
+ )
44
+ except httpx.TimeoutException as exc:
45
+ raise ServiceError("The request timed out. Please try again.") from exc
46
+ except httpx.RequestError as exc:
47
+ raise ServiceError("Could not connect to AITerminal. Check your internet connection and try again.") from exc
48
+ if result.status_code == 401:
49
+ raise ServiceError("The request was not accepted.")
50
+ if result.status_code == 429:
51
+ raise ServiceError(self._error_message(result, "Too many requests. Please wait and try again."))
52
+ if result.status_code >= 500:
53
+ raise ServiceError(self._error_message(result, "AITerminal is temporarily unavailable."))
54
+ if result.status_code != 200:
55
+ raise ServiceError(self._error_message(result, "The request was rejected."))
56
+ try:
57
+ payload = result.json()
58
+ except ValueError as exc:
59
+ raise ServiceError("AITerminal returned an invalid response.") from exc
60
+ answer = payload.get("answer") if isinstance(payload, dict) else None
61
+ if not isinstance(answer, str) or not answer.strip():
62
+ raise ServiceError("AITerminal returned an invalid response.")
63
+ return answer
64
+
65
+ @staticmethod
66
+ def _error_message(result: httpx.Response, fallback: str) -> str:
67
+ try:
68
+ payload = result.json()
69
+ except ValueError:
70
+ return fallback
71
+ message = payload.get("error") if isinstance(payload, dict) else None
72
+ return message if isinstance(message, str) and message else fallback
73
+
74
+ def close(self) -> None:
75
+ self._http.close()
76
+
77
+ def __enter__(self) -> AITerminalClient:
78
+ return self
79
+
80
+ def __exit__(self, *_: object) -> None:
81
+ self.close()
aiterminal/main.py ADDED
@@ -0,0 +1,64 @@
1
+ """Interactive AITerminal CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import typer
6
+
7
+ from .client import AITerminalClient, AITerminalError
8
+ from .terminal import ask_question, console, show_answer
9
+
10
+ app = typer.Typer(add_completion=False, pretty_exceptions_enable=False)
11
+
12
+
13
+ def run_conversation(client: AITerminalClient) -> None:
14
+ history: list[dict[str, str]] = []
15
+ while True:
16
+ try:
17
+ question = ask_question().strip()
18
+ except (EOFError, KeyboardInterrupt):
19
+ console.print("\nGoodbye!")
20
+ return
21
+ command = question.lower()
22
+ if command in {"exit", "quit"}:
23
+ console.print("Goodbye!")
24
+ return
25
+ if command in {"clear", "/clear"}:
26
+ history.clear()
27
+ console.clear()
28
+ console.print("[bold]AITerminal ready![/bold]")
29
+ continue
30
+ if not question:
31
+ continue
32
+
33
+ try:
34
+ with console.status("Thinking...", spinner="dots"):
35
+ answer = client.ask(question, history)
36
+ except AITerminalError as exc:
37
+ console.print(f"[bold red]{exc}[/bold red]")
38
+ continue
39
+
40
+ show_answer(answer)
41
+ history.extend(({"role": "user", "content": question}, {"role": "assistant", "content": answer}))
42
+ del history[:-20]
43
+
44
+
45
+ @app.callback(invoke_without_command=True)
46
+ def main() -> None:
47
+ """Ask questions of the AITerminal cloud service."""
48
+ console.print("Connecting to AITerminal Cloud...")
49
+ try:
50
+ with AITerminalClient() as client:
51
+ console.print("[bold]AITerminal ready![/bold]")
52
+ run_conversation(client)
53
+ except KeyboardInterrupt:
54
+ console.print("\nGoodbye!")
55
+ except ValueError as exc:
56
+ console.print(f"[bold red]{exc}[/bold red]")
57
+
58
+
59
+ def run() -> None:
60
+ app()
61
+
62
+
63
+ if __name__ == "__main__":
64
+ run()
aiterminal/terminal.py ADDED
@@ -0,0 +1,15 @@
1
+ """Small Rich helpers for the interactive terminal."""
2
+
3
+ from rich.console import Console
4
+ from rich.markdown import Markdown
5
+
6
+ console = Console()
7
+
8
+
9
+ def ask_question() -> str:
10
+ return console.input("[bold cyan]You> [/bold cyan]")
11
+
12
+
13
+ def show_answer(answer: str) -> None:
14
+ console.print("[bold green]AI>[/bold green]")
15
+ console.print(Markdown(answer))
@@ -0,0 +1,48 @@
1
+ Metadata-Version: 2.5
2
+ Name: termchat-ai
3
+ Version: 0.1.0
4
+ Summary: A no-login terminal client for the AITerminal cloud AI service
5
+ Author: AITerminal contributors
6
+ Keywords: ai,cli,groq,netlify,terminal
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Environment :: Console
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
13
+ Requires-Python: >=3.10
14
+ Requires-Dist: httpx<1,>=0.27
15
+ Requires-Dist: rich<15,>=13.7
16
+ Requires-Dist: typer<1,>=0.12
17
+ Description-Content-Type: text/markdown
18
+
19
+ # AITerminal CLI
20
+
21
+ This package provides the `aiterminal` terminal command. It connects directly to the public AITerminal chat function; it does not ask users to log in and does not contain a Groq API key.
22
+
23
+ ## Install
24
+
25
+ The package is named `termchat-ai` and is not published yet. After publication:
26
+
27
+ ```powershell
28
+ py -m pip install termchat-ai
29
+ aiterminal
30
+ ```
31
+
32
+ Linux and macOS:
33
+
34
+ ```sh
35
+ python -m pip install termchat-ai
36
+ aiterminal
37
+ ```
38
+
39
+ For development from a checkout:
40
+
41
+ ```sh
42
+ python -m pip install ./cli
43
+ aiterminal
44
+ ```
45
+
46
+ The client uses `https://aiterminal-cloud.netlify.app` by default. Override with `AIT_TERMINAL_API_URL` if needed.
47
+
48
+ Use `clear` or `/clear` to clear the screen and temporary conversation context; use `exit` or `quit` to end the session. Context is memory-only and is discarded when the process exits.
@@ -0,0 +1,8 @@
1
+ aiterminal/__init__.py,sha256=CLC7Fx1FlEGxFn-3Jr3LtxuhNV5qdEWdnjKDd-THCWQ,66
2
+ aiterminal/client.py,sha256=VUjr5OLViClDc_lyH6uGYQS98szSlDf15n5_0U1AphE,3269
3
+ aiterminal/main.py,sha256=4NaeO9zhHxC_JMITGz8-Iz00PAHgWyA2QKMvlT-vn8c,1924
4
+ aiterminal/terminal.py,sha256=MlkQx6zEq4Akn01X7dl_KdA3S7qcmseFj9YQHmE72WM,372
5
+ termchat_ai-0.1.0.dist-info/METADATA,sha256=UMSfCjbUZ4o8w0juc3Sozzb9a2BLwpfGjPt3j7SAGcw,1467
6
+ termchat_ai-0.1.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
7
+ termchat_ai-0.1.0.dist-info/entry_points.txt,sha256=RHuE5f3T4cILXBMlGSVKe9CtfvazOPZSU8MDzP6ZvS4,51
8
+ termchat_ai-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ aiterminal = aiterminal.main:run