prism-tool 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.
prism/__init__.py ADDED
File without changes
prism/cli.py ADDED
@@ -0,0 +1,62 @@
1
+ import typer
2
+ from rich.console import Console
3
+ from rich.table import Table
4
+
5
+ app = typer.Typer()
6
+ console = Console()
7
+
8
+ @app.command()
9
+ def lint(provider: str = "openai-strict"):
10
+ """Lint a tool function for provider compatibility."""
11
+ from prism.linter import lint_tool
12
+
13
+ console.print("\n[bold cyan]Paste your function. Press Enter twice when done.[/bold cyan]\n")
14
+
15
+ lines = []
16
+ while True:
17
+ line = input()
18
+ if line == "" and lines and lines[-1] == "":
19
+ break
20
+ lines.append(line)
21
+
22
+ code = "\n".join(lines)
23
+ namespace = {}
24
+
25
+ try:
26
+ exec(code, namespace)
27
+ except Exception as e:
28
+ console.print(f"[red]Error: {e}[/red]")
29
+ raise typer.Exit()
30
+
31
+ func = None
32
+ for name, obj in namespace.items():
33
+ if callable(obj) and not name.startswith("_"):
34
+ func = obj
35
+ break
36
+
37
+ if not func:
38
+ console.print("[red]No function found.[/red]")
39
+ raise typer.Exit()
40
+
41
+ warnings = lint_tool(func, providers=[provider])
42
+
43
+ if not warnings:
44
+ console.print(f"\n[green]✅ No issues found.[/green]\n")
45
+ return
46
+
47
+ table = Table(title=f"Prism — {provider}", show_lines=True)
48
+ table.add_column("Level", style="red", width=8)
49
+ table.add_column("Param", style="yellow", width=15)
50
+ table.add_column("Issue", width=40)
51
+ table.add_column("Fix", style="green", width=30)
52
+
53
+ for w in warnings:
54
+ table.add_row(w["level"], w["param"], w["issue"], w["fix"])
55
+
56
+ console.print(table)
57
+
58
+ def main():
59
+ app()
60
+
61
+ if __name__ == "__main__":
62
+ main()
prism/linter.py ADDED
@@ -0,0 +1,66 @@
1
+ import inspect
2
+ from typing import get_type_hints, Union, Any
3
+
4
+ def lint_tool(func, providers=["openai-strict"]):
5
+ warnings = []
6
+ sig = inspect.signature(func)
7
+
8
+ try:
9
+ hints = get_type_hints(func)
10
+ except Exception:
11
+ hints = {}
12
+
13
+ for param_name, param in sig.parameters.items():
14
+ if param_name in ("return", "self", "cls"):
15
+ continue
16
+
17
+ has_default = param.default != inspect.Parameter.empty
18
+ annotation = hints.get(param_name)
19
+
20
+ if "openai-strict" in providers and has_default and param.default is not None:
21
+ warnings.append({
22
+ "provider": "openai-strict",
23
+ "level": "ERROR",
24
+ "param": param_name,
25
+ "issue": f"Has default '{param.default}' — OpenAI strict will reject this.",
26
+ "fix": "Remove the default value."
27
+ })
28
+
29
+ if param_name not in hints:
30
+ warnings.append({
31
+ "provider": "all",
32
+ "level": "WARNING",
33
+ "param": param_name,
34
+ "issue": "No type annotation. Providers will reject this.",
35
+ "fix": f"Add a type like: {param_name}: str"
36
+ })
37
+
38
+ if annotation is Any:
39
+ warnings.append({
40
+ "provider": "openai-strict",
41
+ "level": "ERROR",
42
+ "param": param_name,
43
+ "issue": "'Any' type is rejected by OpenAI strict mode.",
44
+ "fix": "Replace with a concrete type like str or dict."
45
+ })
46
+
47
+ if annotation and hasattr(annotation, "__origin__"):
48
+ if annotation.__origin__ is Union:
49
+ warnings.append({
50
+ "provider": "openai-strict",
51
+ "level": "ERROR",
52
+ "param": param_name,
53
+ "issue": "Union types fail in OpenAI strict mode.",
54
+ "fix": "Use a single concrete type."
55
+ })
56
+
57
+ if not func.__doc__:
58
+ warnings.append({
59
+ "provider": "all",
60
+ "level": "WARNING",
61
+ "param": "function",
62
+ "issue": "No docstring. AI uses this as the tool description.",
63
+ "fix": 'Add a docstring: """What this tool does."""'
64
+ })
65
+
66
+ return warnings
@@ -0,0 +1,67 @@
1
+ Metadata-Version: 2.4
2
+ Name: prism-tool
3
+ Version: 0.1.0
4
+ Summary: Catch AI agent tool schema failures before they reach the provider
5
+ Home-page: https://github.com/sanim-science/prism-sdk
6
+ Author: Sanim
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: typer
10
+ Requires-Dist: rich
11
+ Requires-Dist: pydantic
12
+ Requires-Dist: jsonschema
13
+ Dynamic: author
14
+ Dynamic: description
15
+ Dynamic: description-content-type
16
+ Dynamic: home-page
17
+ Dynamic: requires-dist
18
+ Dynamic: requires-python
19
+ Dynamic: summary
20
+
21
+ # Prism
22
+
23
+ Catch AI agent tool schema failures before they reach the provider.
24
+
25
+ ## The Problem
26
+
27
+ You write a tool for your AI agent. It works locally.
28
+ Then it fails with a cryptic 400 error in production.
29
+ No explanation. No hint. Just failure.
30
+
31
+ Prism catches these before they ever reach OpenAI or Anthropic.
32
+
33
+ ## Install
34
+
35
+ pip install prism-sdk
36
+
37
+ ## Quick Start
38
+
39
+ from prism.linter import lint_tool
40
+
41
+ def search_products(query: str, limit: int = 10, category=None):
42
+ pass
43
+
44
+ issues = lint_tool(search_products)
45
+ for issue in issues:
46
+ print(f"[{issue['level']}] {issue['param']}: {issue['issue']}")
47
+
48
+ ## Output
49
+
50
+ [ERROR] limit: Has default '10' — OpenAI strict will reject this.
51
+ [WARNING] category: No type annotation. Providers will reject this.
52
+ [WARNING] function: No docstring. AI uses this as the tool description.
53
+
54
+ ## What It Catches
55
+
56
+ | Issue | Providers Affected |
57
+ |-------|--------------------|
58
+ | Default values not in required[] | OpenAI strict |
59
+ | Union types | OpenAI strict |
60
+ | Missing type annotations | All |
61
+ | Any type | OpenAI strict |
62
+ | Missing docstring | All |
63
+
64
+ ## Status
65
+
66
+ Early development. Built to solve a real problem.
67
+ Issues and feedback welcome.
@@ -0,0 +1,8 @@
1
+ prism/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ prism/cli.py,sha256=asMbFWYbM0X7bY0XbtYh7mwEHxaJQV5y8LUj5gw2OVg,1651
3
+ prism/linter.py,sha256=kg0HdujyVPJjeTnlQ5XCg08LNGJyXVbl1reBeNbzrV0,2355
4
+ prism_tool-0.1.0.dist-info/METADATA,sha256=U13xs0sGf-JO9H2Byp2qaKsqNAyFbGZuetvU0tyYCK4,1745
5
+ prism_tool-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
6
+ prism_tool-0.1.0.dist-info/entry_points.txt,sha256=Kn-Imyf8zRkFPeWfcmS5w1SaQPSzv-hmDi1bBJpXgDY,41
7
+ prism_tool-0.1.0.dist-info/top_level.txt,sha256=ptR0npZTwkVELlV5R3NcfhrvslYW8AvRiFoKmX7tZp4,6
8
+ prism_tool-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ prism = prism.cli:main
@@ -0,0 +1 @@
1
+ prism