scarwise 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.
@@ -0,0 +1,6 @@
1
+ Metadata-Version: 2.4
2
+ Name: scarwise
3
+ Version: 0.1.0
4
+ Summary: Your AI agents stop repeating the same tool mistakes – Python SDK (needs the scarwise CLI: npm install -g scarwise).
5
+ License: Proprietary – see LICENSE
6
+ Requires-Python: >=3.9
@@ -0,0 +1,13 @@
1
+ [project]
2
+ name = "scarwise"
3
+ version = "0.1.0"
4
+ description = "Your AI agents stop repeating the same tool mistakes – Python SDK (needs the scarwise CLI: npm install -g scarwise)."
5
+ requires-python = ">=3.9"
6
+ license = { text = "Proprietary – see LICENSE" }
7
+
8
+ [build-system]
9
+ requires = ["setuptools>=68"]
10
+ build-backend = "setuptools.build_meta"
11
+
12
+ [tool.setuptools]
13
+ packages = ["scarwise"]
@@ -0,0 +1,117 @@
1
+ """Scarwise for Python applications that call tools themselves (socket C).
2
+
3
+ from scarwise import guard, ScarwiseStop
4
+
5
+ @guard("run_command")
6
+ def run_command(command: str): ...
7
+
8
+ Before each call: a known mistake raises ScarwiseStop (the tool does not run); a verified
9
+ correction changes the input. After: a known error gets the fix appended to its message.
10
+ Any problem inside Scarwise lets the tool run normally. The decisions come from the same
11
+ engine as every other agent (`scarwise decide`), so lessons are shared.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import functools
16
+ import inspect
17
+ import json
18
+ import os
19
+ import shutil
20
+ import subprocess
21
+ import uuid
22
+
23
+ __all__ = ["guard", "ScarwiseStop"]
24
+ __version__ = "0.1.0"
25
+
26
+ _SESSION = "sdk-python:" + uuid.uuid4().hex
27
+
28
+
29
+ class ScarwiseStop(Exception):
30
+ """A known mistake was stopped before the tool ran; the message says what worked here."""
31
+
32
+ def __init__(self, reason: str, lesson_id: int | None = None):
33
+ super().__init__(reason)
34
+ self.lesson_id = lesson_id
35
+
36
+
37
+ def _command() -> list[str] | None:
38
+ cli = os.environ.get("SCARWISE_CLI")
39
+ if cli:
40
+ return ["node", cli] if cli.endswith((".mjs", ".js", ".ts")) else [cli]
41
+ found = shutil.which("scarwise")
42
+ return [found] if found else None
43
+
44
+
45
+ def _decide(event: dict) -> dict:
46
+ cmd = _command()
47
+ if not cmd:
48
+ return {"kind": "none"}
49
+ try:
50
+ r = subprocess.run(cmd + ["decide"], input=json.dumps(event), capture_output=True, text=True, timeout=10)
51
+ return json.loads(r.stdout or '{"kind":"none"}')
52
+ except Exception: # fail-open: Scarwise never blocks a tool by accident
53
+ return {"kind": "none"}
54
+
55
+
56
+ def _bind(fn, args, kwargs) -> dict:
57
+ try:
58
+ bound = inspect.signature(fn).bind(*args, **kwargs)
59
+ bound.apply_defaults()
60
+ return {k: v for k, v in bound.arguments.items() if isinstance(v, (str, int, float, bool, list, dict, type(None)))}
61
+ except TypeError:
62
+ return {"args": [a for a in args if isinstance(a, (str, int, float, bool))]}
63
+
64
+
65
+ def guard(name: str, agent: str = "sdk-python", session: str | None = None):
66
+ """Decorator: guards a tool function (sync or async) with Scarwise lessons."""
67
+
68
+ def deco(fn):
69
+ sess = session or _SESSION
70
+
71
+ def before(args, kwargs):
72
+ data = _bind(fn, args, kwargs)
73
+ d = _decide({"agent": agent, "phase": "pre", "session": sess, "tool": name, "input": data})
74
+ if d.get("kind") == "deny":
75
+ raise ScarwiseStop(d.get("reason", "Scarwise: known mistake"), d.get("lessonId"))
76
+ if d.get("kind") == "rewrite" and isinstance(d.get("updatedInput"), dict):
77
+ params = inspect.signature(fn).parameters
78
+ new = {k: v for k, v in d["updatedInput"].items() if k in params}
79
+ if new:
80
+ data = {**data, **new}
81
+ return (), data, data
82
+ return args, kwargs, data
83
+
84
+ def after_error(e, data):
85
+ d = _decide({"agent": agent, "phase": "failure", "session": sess, "tool": name, "input": data, "error": str(e)})
86
+ if d.get("kind") == "context" and e.args:
87
+ e.args = (f"{e.args[0]}\n{d.get('reason', '')}",) + tuple(e.args[1:])
88
+
89
+ def after_ok(result, data):
90
+ _decide({"agent": agent, "phase": "post", "session": sess, "tool": name, "input": data, "output": str(result)[:4000]})
91
+
92
+ if inspect.iscoroutinefunction(fn):
93
+ @functools.wraps(fn)
94
+ async def awrapper(*args, **kwargs):
95
+ args, kwargs, data = before(args, kwargs)
96
+ try:
97
+ result = await fn(*args, **kwargs)
98
+ except Exception as e:
99
+ after_error(e, data)
100
+ raise
101
+ after_ok(result, data)
102
+ return result
103
+ return awrapper
104
+
105
+ @functools.wraps(fn)
106
+ def wrapper(*args, **kwargs):
107
+ args, kwargs, data = before(args, kwargs)
108
+ try:
109
+ result = fn(*args, **kwargs)
110
+ except Exception as e:
111
+ after_error(e, data)
112
+ raise
113
+ after_ok(result, data)
114
+ return result
115
+ return wrapper
116
+
117
+ return deco
@@ -0,0 +1,6 @@
1
+ Metadata-Version: 2.4
2
+ Name: scarwise
3
+ Version: 0.1.0
4
+ Summary: Your AI agents stop repeating the same tool mistakes – Python SDK (needs the scarwise CLI: npm install -g scarwise).
5
+ License: Proprietary – see LICENSE
6
+ Requires-Python: >=3.9
@@ -0,0 +1,6 @@
1
+ pyproject.toml
2
+ scarwise/__init__.py
3
+ scarwise.egg-info/PKG-INFO
4
+ scarwise.egg-info/SOURCES.txt
5
+ scarwise.egg-info/dependency_links.txt
6
+ scarwise.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ scarwise
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+