relay-code 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.
- agent/__init__.py +6 -0
- agent/agent.py +162 -0
- agent/events.py +133 -0
- agent/session.py +71 -0
- client/__init__.py +23 -0
- client/llm_client.py +236 -0
- client/response.py +83 -0
- config/__init__.py +6 -0
- config/config.py +76 -0
- config/credentials.py +54 -0
- config/loader.py +108 -0
- config/oauth.py +137 -0
- context/__init__.py +5 -0
- context/manager.py +92 -0
- main.py +231 -0
- prompts/__init__.py +0 -0
- prompts/system.py +350 -0
- relay_code-0.1.0.dist-info/METADATA +64 -0
- relay_code-0.1.0.dist-info/RECORD +50 -0
- relay_code-0.1.0.dist-info/WHEEL +5 -0
- relay_code-0.1.0.dist-info/entry_points.txt +2 -0
- relay_code-0.1.0.dist-info/licenses/LICENSE +674 -0
- relay_code-0.1.0.dist-info/top_level.txt +9 -0
- tools/__init__.py +14 -0
- tools/base.py +188 -0
- tools/core/__init__.py +44 -0
- tools/core/directories.py +65 -0
- tools/core/edit.py +162 -0
- tools/core/glob.py +92 -0
- tools/core/grep.py +128 -0
- tools/core/read.py +129 -0
- tools/core/shell.py +162 -0
- tools/core/todo.py +135 -0
- tools/core/write.py +76 -0
- tools/memory/__init__.py +0 -0
- tools/memory/memory.py +191 -0
- tools/network/__init__.py +0 -0
- tools/network/fetch.py +72 -0
- tools/network/search.py +72 -0
- tools/registry.py +96 -0
- ui/__init__.py +5 -0
- ui/app.py +949 -0
- ui/format.py +145 -0
- ui/logo.py +46 -0
- ui/renderer.py +580 -0
- ui/theme.py +60 -0
- utils/__init__.py +17 -0
- utils/errors.py +47 -0
- utils/paths.py +44 -0
- utils/text.py +74 -0
utils/paths.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def resolve_path(base: str | Path, path: str | Path) -> Path:
|
|
5
|
+
base_path = Path(base).resolve()
|
|
6
|
+
candidate = Path(path)
|
|
7
|
+
resolved = candidate.resolve() if candidate.is_absolute() else (base_path / candidate).resolve()
|
|
8
|
+
try:
|
|
9
|
+
resolved.relative_to(base_path)
|
|
10
|
+
except ValueError as exc:
|
|
11
|
+
raise ValueError(f"Path is outside the working directory: {path}") from exc
|
|
12
|
+
return resolved
|
|
13
|
+
|
|
14
|
+
def display_path_relative_to_cwd(path: str, cwd: Path | None) -> str:
|
|
15
|
+
try:
|
|
16
|
+
p = Path(path)
|
|
17
|
+
except Exception:
|
|
18
|
+
return path
|
|
19
|
+
|
|
20
|
+
if cwd:
|
|
21
|
+
try:
|
|
22
|
+
return str(p.relative_to(cwd))
|
|
23
|
+
except ValueError:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
return str(p)
|
|
27
|
+
|
|
28
|
+
def is_binary_file(path: str | Path) -> bool:
|
|
29
|
+
try:
|
|
30
|
+
with open(path, "rb") as f:
|
|
31
|
+
chunk = f.read(8192)
|
|
32
|
+
return b"\x00" in chunk
|
|
33
|
+
except (OSError, IOError):
|
|
34
|
+
return False
|
|
35
|
+
|
|
36
|
+
def ensure_parent_dir(path: str | Path) -> Path:
|
|
37
|
+
path = Path(path)
|
|
38
|
+
|
|
39
|
+
path.parent.mkdir(
|
|
40
|
+
parents=True,
|
|
41
|
+
exist_ok=True
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
return path
|
utils/text.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import tiktoken
|
|
2
|
+
|
|
3
|
+
def get_tokenizer(model: str):
|
|
4
|
+
try:
|
|
5
|
+
return tiktoken.encoding_for_model(model)
|
|
6
|
+
except Exception:
|
|
7
|
+
return tiktoken.get_encoding("cl100k_base")
|
|
8
|
+
|
|
9
|
+
def count_tokens(text: str, model: str = "") -> int:
|
|
10
|
+
tokenizer = get_tokenizer(model)
|
|
11
|
+
|
|
12
|
+
if tokenizer:
|
|
13
|
+
return len(tokenizer.encode(text))
|
|
14
|
+
|
|
15
|
+
return estimate_tokens(text)
|
|
16
|
+
|
|
17
|
+
def estimate_tokens(text: str) -> int:
|
|
18
|
+
return max(1, len(text) // 4) # on avg 1 token is ~ 4 characters
|
|
19
|
+
|
|
20
|
+
def truncate_text(
|
|
21
|
+
text: str,
|
|
22
|
+
model: str,
|
|
23
|
+
max_tokens: int,
|
|
24
|
+
suffix: str = "\n...[truncated]",
|
|
25
|
+
preserve_lines: bool = True,
|
|
26
|
+
):
|
|
27
|
+
|
|
28
|
+
current_tokens = count_tokens(text, model)
|
|
29
|
+
|
|
30
|
+
if current_tokens <= max_tokens:
|
|
31
|
+
return text
|
|
32
|
+
|
|
33
|
+
suffix_tokens = count_tokens(suffix, model)
|
|
34
|
+
target_tokens = max_tokens - suffix_tokens
|
|
35
|
+
|
|
36
|
+
if target_tokens <= 0:
|
|
37
|
+
return suffix.strip()
|
|
38
|
+
|
|
39
|
+
if preserve_lines:
|
|
40
|
+
return _truncate_by_lines(text, target_tokens, suffix, model)
|
|
41
|
+
else:
|
|
42
|
+
return _truncate_by_chars(text, target_tokens, suffix, model)
|
|
43
|
+
|
|
44
|
+
def _truncate_by_lines(text: str, target_tokens: int, suffix: str, model: str) -> str:
|
|
45
|
+
lines = text.split("\n")
|
|
46
|
+
result_lines: list[str] = []
|
|
47
|
+
current_tokens = 0
|
|
48
|
+
|
|
49
|
+
for line in lines:
|
|
50
|
+
line_tokens = count_tokens(line + "\n", model)
|
|
51
|
+
if current_tokens + line_tokens > target_tokens:
|
|
52
|
+
break
|
|
53
|
+
result_lines.append(line)
|
|
54
|
+
current_tokens += line_tokens
|
|
55
|
+
|
|
56
|
+
if not result_lines:
|
|
57
|
+
# Fall back to character truncation if no complete lines fit
|
|
58
|
+
return _truncate_by_chars(text, target_tokens, suffix, model)
|
|
59
|
+
|
|
60
|
+
return "\n".join(result_lines) + suffix
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _truncate_by_chars(text: str, target_tokens: int, suffix: str, model: str) -> str:
|
|
64
|
+
# Binary search for the right length
|
|
65
|
+
low, high = 0, len(text)
|
|
66
|
+
|
|
67
|
+
while low < high:
|
|
68
|
+
mid = (low + high + 1) // 2
|
|
69
|
+
if count_tokens(text[:mid], model) <= target_tokens:
|
|
70
|
+
low = mid
|
|
71
|
+
else:
|
|
72
|
+
high = mid - 1
|
|
73
|
+
|
|
74
|
+
return text[:low] + suffix
|