kubrick-cli 0.1.2__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.
@@ -0,0 +1,192 @@
1
+ """Triton client for streaming LLM requests using HTTP only (no dependencies)."""
2
+
3
+ import http.client
4
+ import json
5
+ import ssl
6
+ from typing import Dict, Iterator, List
7
+ from urllib.parse import urlparse
8
+
9
+
10
+ class TritonLLMClient:
11
+ """Client for interacting with Triton LLM backend using HTTP (no extra dependencies)."""
12
+
13
+ def __init__(
14
+ self,
15
+ url: str = "localhost:8000",
16
+ model_name: str = "llm_decoupled",
17
+ ):
18
+ """
19
+ Initialize Triton LLM client.
20
+
21
+ Args:
22
+ url: Triton server URL (host:port, default: localhost:8000)
23
+ model_name: Name of the Triton model to use
24
+ """
25
+ self.model_name = model_name
26
+
27
+ # Parse URL
28
+ if not url.startswith("http://") and not url.startswith("https://"):
29
+ url = f"http://{url}"
30
+
31
+ parsed = urlparse(url)
32
+ self.is_https = parsed.scheme == "https"
33
+ self.host = parsed.hostname or "localhost"
34
+ self.port = parsed.port or (443 if self.is_https else 8000)
35
+ self.timeout = 600
36
+ self.url = f"{parsed.scheme}://{self.host}:{self.port}"
37
+
38
+ def _get_connection(self) -> http.client.HTTPConnection:
39
+ """Create an HTTP(S) connection."""
40
+ if self.is_https:
41
+ context = ssl.create_default_context()
42
+ return http.client.HTTPSConnection(
43
+ self.host, self.port, timeout=self.timeout, context=context
44
+ )
45
+ else:
46
+ return http.client.HTTPConnection(
47
+ self.host, self.port, timeout=self.timeout
48
+ )
49
+
50
+ def generate_streaming(
51
+ self,
52
+ messages: List[Dict[str, str]],
53
+ stream_options: Dict = None,
54
+ ) -> Iterator[str]:
55
+ """
56
+ Generate streaming response from LLM.
57
+
58
+ Args:
59
+ messages: List of message dicts with 'role' and 'content'
60
+ stream_options: Optional streaming parameters
61
+
62
+ Yields:
63
+ Text chunks as they arrive
64
+ """
65
+ # Build request payload
66
+ if stream_options is None:
67
+ stream_options = {"streaming": True}
68
+ else:
69
+ stream_options = {"streaming": True, **stream_options}
70
+
71
+ # Build the request payload using the custom generate_stream format
72
+ payload = {
73
+ "text_input": json.dumps(messages),
74
+ "parameters": stream_options,
75
+ }
76
+
77
+ headers = {
78
+ "Content-Type": "application/json",
79
+ "Accept": "text/event-stream",
80
+ }
81
+
82
+ body = json.dumps(payload).encode("utf-8")
83
+ path = f"/v2/models/{self.model_name}/generate_stream"
84
+
85
+ conn = None
86
+ try:
87
+ conn = self._get_connection()
88
+ conn.request("POST", path, body=body, headers=headers)
89
+ response = conn.getresponse()
90
+
91
+ if response.status not in (200, 201):
92
+ error_body = response.read().decode("utf-8")
93
+ raise Exception(
94
+ f"Server returned {response.status}: {error_body}"
95
+ )
96
+
97
+ # Read streaming response
98
+ buffer = ""
99
+ while True:
100
+ chunk = response.read(1024)
101
+ if not chunk:
102
+ break
103
+
104
+ if isinstance(chunk, bytes):
105
+ chunk = chunk.decode("utf-8")
106
+
107
+ buffer += chunk
108
+
109
+ # Process complete lines
110
+ while "\n" in buffer:
111
+ line, buffer = buffer.split("\n", 1)
112
+ line = line.strip()
113
+
114
+ if not line:
115
+ continue
116
+
117
+ # Remove SSE "data: " prefix if present
118
+ if line.startswith("data: "):
119
+ line = line[6:]
120
+
121
+ # Check for end signal
122
+ if line == "[DONE]":
123
+ return
124
+
125
+ try:
126
+ data = json.loads(line)
127
+
128
+ # Handle both text_output and outputs formats
129
+ output_data = None
130
+ if "text_output" in data:
131
+ output_data = data["text_output"]
132
+ elif "outputs" in data and len(data["outputs"]) > 0:
133
+ output_data = data["outputs"][0].get("data", [""])[
134
+ 0
135
+ ]
136
+
137
+ if output_data:
138
+ # Parse the actual response
139
+ try:
140
+ chunk_data = json.loads(output_data)
141
+
142
+ if chunk_data.get("type") == "chunk":
143
+ yield chunk_data.get("content", "")
144
+ elif chunk_data.get("type") == "complete":
145
+ # Stream is complete
146
+ return
147
+ elif chunk_data.get("type") == "error":
148
+ raise Exception(
149
+ f"LLM error: {chunk_data.get('content')}"
150
+ )
151
+ except (json.JSONDecodeError, TypeError):
152
+ # If not JSON, treat as plain text chunk
153
+ yield output_data
154
+
155
+ except json.JSONDecodeError:
156
+ # If line is not JSON, skip it
157
+ continue
158
+
159
+ finally:
160
+ if conn:
161
+ conn.close()
162
+
163
+ def generate(
164
+ self,
165
+ messages: List[Dict[str, str]],
166
+ stream_options: Dict = None,
167
+ ) -> str:
168
+ """
169
+ Generate non-streaming response from LLM.
170
+
171
+ Args:
172
+ messages: List of message dicts with 'role' and 'content'
173
+ stream_options: Optional parameters
174
+
175
+ Returns:
176
+ Complete response text
177
+ """
178
+ chunks = []
179
+ for chunk in self.generate_streaming(messages, stream_options):
180
+ chunks.append(chunk)
181
+ return "".join(chunks)
182
+
183
+ def is_healthy(self) -> bool:
184
+ """Check if Triton server is healthy."""
185
+ try:
186
+ conn = self._get_connection()
187
+ conn.request("GET", "/v2/health/live")
188
+ response = conn.getresponse()
189
+ conn.close()
190
+ return response.status == 200
191
+ except Exception:
192
+ return False
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.4
2
+ Name: kubrick-cli
3
+ Version: 0.1.2
4
+ Summary: A CLI tool for AI-assisted coding using Triton LLM backend
5
+ Author-email: Russell Land <rcland12@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/rcland12/kubrick-cli
8
+ Project-URL: Repository, https://github.com/rcland12/kubrick-cli
9
+ Project-URL: Issues, https://github.com/rcland12/kubrick-cli/issues
10
+ Requires-Python: >=3.8
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: rich>=13.0.0
14
+ Requires-Dist: prompt_toolkit>=3.0.0
15
+ Provides-Extra: dev
16
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
17
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
18
+ Requires-Dist: black>=22.0.0; extra == "dev"
19
+ Requires-Dist: build>=1.0.0; extra == "dev"
20
+ Requires-Dist: twine>=5.0.0; extra == "dev"
21
+ Dynamic: license-file
22
+
23
+ # Kubrick CLI
24
+
25
+ An AI-assisted coding CLI tool powered by your own Triton LLM backend. Like Claude Code, but self-hosted.
26
+
27
+ ## Installation
28
+
29
+ ### Local Installation
30
+
31
+ ```bash
32
+ pip install -e .
33
+ kubrick
34
+ ```
35
+
36
+ ### Docker
37
+
38
+ ```bash
39
+ cd /path/to/your/project
40
+ docker run --rm -it \
41
+ --network host \
42
+ -v ${HOME}:/home/kubrick \
43
+ -v ${PWD}:/workspace \
44
+ -v /etc/localtime:/etc/localtime:ro \
45
+ -v /etc/timezone:/etc/timezone:ro \
46
+ rcland12/kubrick-cli
47
+ ```
48
+
49
+ ## Quick Start
50
+
51
+ ```bash
52
+ # Basic usage
53
+ kubrick
54
+
55
+ # Custom Triton server
56
+ kubrick --triton-url my-server:8000
57
+
58
+ # Load previous conversation
59
+ kubrick --load 20240118_143022
60
+ ```
61
+
62
+ ## Requirements
63
+
64
+ - Python 3.8+
65
+ - Triton Inference Server with streaming LLM model
66
+ - Default endpoint: `http://localhost:8000`
67
+ - Default model: `llm_decoupled`
68
+
69
+ ## Documentation
70
+
71
+ - **[WIKI.md](docs/WIKI.md)** - Complete features, commands, and usage guide
72
+ - **[TRITON.md](docs/TRITON.md)** - Triton backend setup and requirements
73
+ - **[DOCKER.md](docs/DOCKER.md)** - Docker setup and troubleshooting
74
+
75
+ ## Configuration
76
+
77
+ Config stored at `~/.kubrick/config.json`. Override with environment variables:
78
+
79
+ ```bash
80
+ export TRITON_URL=localhost:8000
81
+ export TRITON_MODEL_NAME=llm_decoupled
82
+ ```
83
+
84
+ ## License
85
+
86
+ MIT License - See [LICENSE](LICENSE)
@@ -0,0 +1,26 @@
1
+ kubrick_cli/__init__.py,sha256=UZSaa0ajEH_EkbP5I239WDL8cUvBaxW2wP7yz_C2VU4,1403
2
+ kubrick_cli/agent_loop.py,sha256=DOm0URQeyALgLUWKNRSzRjxBRobOvQoB-4XAOc7slzo,10468
3
+ kubrick_cli/classifier.py,sha256=KVstxN-PdeZz0zCpzziQpqPwaT6bj9QORabqN-Ok6qY,5359
4
+ kubrick_cli/config.py,sha256=CZ6P6oxKjaEkAjayLlVIQlZ0xkAHdfc_bxVZlaIU-MQ,8518
5
+ kubrick_cli/display.py,sha256=GFrrDZpFeVRIuqLJLI_mHyCuH2FbKiiBfMpQFQbBOW8,5912
6
+ kubrick_cli/execution_strategy.py,sha256=1OK6ThFedsQgmdPGlClh5VWi_ydLile8jM1_31FAQBQ,6346
7
+ kubrick_cli/main.py,sha256=mTDLk1WyPo1hYb3XftD54axqWKFDyCAWomrdeuotiqg,27538
8
+ kubrick_cli/planning.py,sha256=bdWfOrfPh383FBmfLVE6QneRRLlbOzJvY1KlFfA5fbs,9222
9
+ kubrick_cli/progress.py,sha256=D9C1Algx_aJzd_jCyBztclPRhfveUTAaidh5SGCWwCE,4764
10
+ kubrick_cli/safety.py,sha256=01HnowDZInRIho9Y09TzrYjURl_GzLj7YVvtxH8xWvU,6510
11
+ kubrick_cli/scheduler.py,sha256=2S6FH38atVlMnDekVgGNzKvlX1mwbJCF2DEpYmnWOt8,6043
12
+ kubrick_cli/setup_wizard.py,sha256=yBjDey0nl-MtRF-XMZalWtcKqfoGwmfCkX7o6cpOGUQ,5129
13
+ kubrick_cli/tools.py,sha256=5RnCK6xfV_pSaKJ-T9PklVDy_rBFoTa6nIpPr_QBxYQ,13725
14
+ kubrick_cli/triton_client.py,sha256=XV9_s9nuNNfX840CCz0lc-7rv_-AzZXiwQg1cDg8o0I,6419
15
+ kubrick_cli/providers/__init__.py,sha256=pI8TVid6LCEG0VMd4H-Oo-gO-40CG80gT9XZxc5Dnak,212
16
+ kubrick_cli/providers/anthropic_provider.py,sha256=Qsmy2bHtBgxsRYbvbRWvkhK168M2xA7cxb-EBgzIxNw,6982
17
+ kubrick_cli/providers/base.py,sha256=K7iC00pBaHuHpbIx-61u4f_Cy422XrWuHS8upkVg1tY,3847
18
+ kubrick_cli/providers/factory.py,sha256=6pkD_cVfT5Dsiar08t3NoLJnrbP3Pggp01bJdE7Uf3A,6330
19
+ kubrick_cli/providers/openai_provider.py,sha256=Q8ApgcmS6s0Y8a03Dx4e5QQFwXyonBNK6OLHAyoxcX8,5778
20
+ kubrick_cli/providers/triton_provider.py,sha256=9UM76TRJAHDeYUFItvVEXmMvxex1yzJ-iRwyRhKtUDY,2735
21
+ kubrick_cli-0.1.2.dist-info/licenses/LICENSE,sha256=Vap47V4-2uRylukx31JcYcYSD7QzURHn70rLxmFyenI,1073
22
+ kubrick_cli-0.1.2.dist-info/METADATA,sha256=I8mz2veI5LuWX_JwSW3K8M_V7bsMrIwwkNUnUQ6tWxs,2051
23
+ kubrick_cli-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
+ kubrick_cli-0.1.2.dist-info/entry_points.txt,sha256=4XyhYn1bMdkgqTxwvvGe1LAtby-2lP4JaIAZUwBkP1k,50
25
+ kubrick_cli-0.1.2.dist-info/top_level.txt,sha256=BUz1Y_kmuEM-kvPHdyv2dHO5X1dUHfmE8Mhzr0gdEkQ,12
26
+ kubrick_cli-0.1.2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ kubrick = kubrick_cli.main:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [2026] [Russell Land]
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 @@
1
+ kubrick_cli