seif-cli 0.3.3__tar.gz → 0.3.5__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.
Files changed (85) hide show
  1. {seif_cli-0.3.3/src/seif_cli.egg-info → seif_cli-0.3.5}/PKG-INFO +1 -1
  2. {seif_cli-0.3.3 → seif_cli-0.3.5}/pyproject.toml +1 -1
  3. seif_cli-0.3.5/src/seif/bridge/native_client.py +212 -0
  4. {seif_cli-0.3.3 → seif_cli-0.3.5/src/seif_cli.egg-info}/PKG-INFO +1 -1
  5. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif_cli.egg-info/SOURCES.txt +1 -0
  6. {seif_cli-0.3.3 → seif_cli-0.3.5}/LICENSE +0 -0
  7. {seif_cli-0.3.3 → seif_cli-0.3.5}/MANIFEST.in +0 -0
  8. {seif_cli-0.3.3 → seif_cli-0.3.5}/README.md +0 -0
  9. {seif_cli-0.3.3 → seif_cli-0.3.5}/setup.cfg +0 -0
  10. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/__init__.py +0 -0
  11. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/__main__.py +0 -0
  12. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/analysis/__init__.py +0 -0
  13. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/analysis/physical_constants.py +0 -0
  14. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/analysis/quality_gate.py +0 -0
  15. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/analysis/stance_detector.py +0 -0
  16. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/analysis/transcompiler.py +0 -0
  17. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/bridge/__init__.py +0 -0
  18. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/bridge/telegram_bot.py +0 -0
  19. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/cli/__init__.py +0 -0
  20. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/cli/__main__.py +0 -0
  21. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/cli/chat.py +0 -0
  22. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/cli/cli.py +0 -0
  23. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/cli/identity.py +0 -0
  24. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/cli/main.py +0 -0
  25. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/cli/resonance_display.py +0 -0
  26. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/cli/serve.py +0 -0
  27. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/cli/serve_v2.py +0 -0
  28. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/cli/wrapper.py +0 -0
  29. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/constants.py +0 -0
  30. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/__init__.py +0 -0
  31. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/advisor.py +0 -0
  32. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/code_compressor.py +0 -0
  33. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/context_bridge.py +0 -0
  34. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/context_importer.py +0 -0
  35. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/context_manager.py +0 -0
  36. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/context_qr.py +0 -0
  37. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/cycle.py +0 -0
  38. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/file_extractor.py +0 -0
  39. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/git_context.py +0 -0
  40. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/git_hooks.py +0 -0
  41. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/ingest.py +0 -0
  42. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/nucleus.py +0 -0
  43. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/ref.py +0 -0
  44. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/registry.py +0 -0
  45. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/seif_io.py +0 -0
  46. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/sessions.py +0 -0
  47. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/context/workspace.py +0 -0
  48. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/core/__init__.py +0 -0
  49. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/core/fingerprint.py +0 -0
  50. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/core/resonance_encoding.py +0 -0
  51. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/core/resonance_gate.py +0 -0
  52. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/core/resonance_signal.py +0 -0
  53. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/core/signing.py +0 -0
  54. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/core/timestamping.py +0 -0
  55. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/core/transfer_function.py +0 -0
  56. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/core/triple_gate.py +0 -0
  57. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/data/RESONANCE.json +0 -0
  58. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/data/__init__.py +0 -0
  59. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/data/defaults/__init__.py +0 -0
  60. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/data/paths.py +0 -0
  61. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/security/__init__.py +0 -0
  62. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif/security/mode.py +0 -0
  63. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif_cli.egg-info/dependency_links.txt +0 -0
  64. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif_cli.egg-info/entry_points.txt +0 -0
  65. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif_cli.egg-info/requires.txt +0 -0
  66. {seif_cli-0.3.3 → seif_cli-0.3.5}/src/seif_cli.egg-info/top_level.txt +0 -0
  67. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_advisor.py +0 -0
  68. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_canonical_inputs.py +0 -0
  69. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_code_compressor.py +0 -0
  70. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_collaborative_seif.py +0 -0
  71. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_context_qr.py +0 -0
  72. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_context_repo.py +0 -0
  73. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_git_context.py +0 -0
  74. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_git_hooks.py +0 -0
  75. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_init.py +0 -0
  76. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_quality_gate.py +0 -0
  77. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_ref.py +0 -0
  78. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_registry.py +0 -0
  79. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_resonance_gate.py +0 -0
  80. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_seif_io.py +0 -0
  81. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_stance_detector.py +0 -0
  82. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_transcompiler.py +0 -0
  83. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_transfer_function.py +0 -0
  84. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_triple_gate.py +0 -0
  85. {seif_cli-0.3.3 → seif_cli-0.3.5}/tests/test_workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seif-cli
3
- Version: 0.3.3
3
+ Version: 0.3.5
4
4
  Summary: Measure AI output quality, protect sensitive data, watch your AI environment resonate. Quality Gate, Classification, Sentinel & Auto-Healing, SEIF OS.
5
5
  Author: André Cunha Antero de Carvalho
6
6
  License: CC-BY-NC-SA-4.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "seif-cli"
7
- version = "0.3.3"
7
+ version = "0.3.5"
8
8
  description = "Measure AI output quality, protect sensitive data, watch your AI environment resonate. Quality Gate, Classification, Sentinel & Auto-Healing, SEIF OS."
9
9
  readme = "README.md"
10
10
  license = {text = "CC-BY-NC-SA-4.0"}
@@ -0,0 +1,212 @@
1
+ """
2
+ Native AI Client — Direct SDK integration with Claude CLI fallback.
3
+
4
+ Supports streaming, multi-backend routing, and context injection from
5
+ the personal nucleus. When no ANTHROPIC_API_KEY is set, uses Claude CLI
6
+ (compatible with Max Plan via OAuth).
7
+ """
8
+
9
+ from dataclasses import dataclass
10
+ from typing import Iterator, Optional
11
+ import os
12
+ import shutil
13
+ import subprocess
14
+
15
+
16
+ @dataclass
17
+ class ChatConfig:
18
+ backend: str = "auto" # claude, claude-cli, gemini, local, auto
19
+ model: str = "" # model override (empty = use default)
20
+ stream: bool = True # streaming responses
21
+ quality_gate: bool = True # measure every response
22
+ max_tokens: int = 4096
23
+
24
+
25
+ # Default models per backend
26
+ _DEFAULT_MODELS = {
27
+ "claude": "claude-sonnet-4-6",
28
+ "claude-cli": "claude-sonnet-4-6",
29
+ "gemini": "gemini-2.5-flash",
30
+ "grok": "grok-4-1-fast",
31
+ "local": "llama3",
32
+ }
33
+
34
+
35
+ def _has_claude_cli() -> bool:
36
+ return shutil.which("claude") is not None
37
+
38
+
39
+ class NativeClient:
40
+ """Multi-backend AI client with streaming support."""
41
+
42
+ def __init__(self, config: Optional[ChatConfig] = None,
43
+ profile: Optional[dict] = None):
44
+ self.config = config or ChatConfig()
45
+ self.profile = profile or {}
46
+ self._backend = self._resolve_backend()
47
+ self._model = self.config.model or _DEFAULT_MODELS.get(self._backend, "")
48
+ self._client = None
49
+
50
+ def _resolve_backend(self) -> str:
51
+ """Resolve 'auto' to the best available backend."""
52
+ if self.config.backend != "auto":
53
+ return self.config.backend
54
+
55
+ profile_default = self.profile.get("default_backend", "")
56
+ if profile_default and profile_default != "auto":
57
+ return profile_default
58
+
59
+ # SDK requires API key with credits
60
+ if os.environ.get("ANTHROPIC_API_KEY"):
61
+ return "claude"
62
+ # No API key — prefer Claude CLI (Max Plan compatible)
63
+ if _has_claude_cli():
64
+ return "claude-cli"
65
+ if os.environ.get("GEMINI_API_KEY") or os.environ.get("GOOGLE_API_KEY"):
66
+ return "gemini"
67
+ return "claude"
68
+
69
+ def _get_anthropic_client(self):
70
+ """Lazy-init Anthropic client."""
71
+ if self._client is None:
72
+ import anthropic
73
+ self._client = anthropic.Anthropic()
74
+ return self._client
75
+
76
+ def send(self, message: str, history: list[dict] = None,
77
+ system: str = "") -> str:
78
+ """Send message and return complete response."""
79
+ if self._backend == "claude":
80
+ return self._send_claude(message, history or [], system)
81
+ elif self._backend == "claude-cli":
82
+ return self._send_claude_cli(message, history or [], system)
83
+ elif self._backend == "gemini":
84
+ return self._send_gemini(message, history or [], system)
85
+ elif self._backend == "local":
86
+ return self._send_local(message, history or [], system)
87
+ else:
88
+ return f"Backend '{self._backend}' not supported."
89
+
90
+ def stream(self, message: str, history: list[dict] = None,
91
+ system: str = "") -> Iterator[str]:
92
+ """Stream response tokens."""
93
+ if self._backend == "claude":
94
+ yield from self._stream_claude(message, history or [], system)
95
+ elif self._backend == "claude-cli":
96
+ yield from self._stream_claude_cli(message, history or [], system)
97
+ else:
98
+ yield self.send(message, history, system)
99
+
100
+ # -- Claude SDK ------------------------------------------------
101
+
102
+ def _send_claude(self, message: str, history: list[dict],
103
+ system: str) -> str:
104
+ try:
105
+ client = self._get_anthropic_client()
106
+ messages = history + [{"role": "user", "content": message}]
107
+ response = client.messages.create(
108
+ model=self._model,
109
+ max_tokens=self.config.max_tokens,
110
+ system=system,
111
+ messages=messages,
112
+ )
113
+ return response.content[0].text
114
+ except ImportError:
115
+ return self._send_claude_cli(message, history, system)
116
+ except Exception as e:
117
+ return f"Error: {e}"
118
+
119
+ def _stream_claude(self, message: str, history: list[dict],
120
+ system: str) -> Iterator[str]:
121
+ try:
122
+ client = self._get_anthropic_client()
123
+ messages = history + [{"role": "user", "content": message}]
124
+ with client.messages.stream(
125
+ model=self._model,
126
+ max_tokens=self.config.max_tokens,
127
+ system=system,
128
+ messages=messages,
129
+ ) as stream:
130
+ for text in stream.text_stream:
131
+ yield text
132
+ except ImportError:
133
+ yield from self._stream_claude_cli(message, history, system)
134
+ except Exception as e:
135
+ yield f"Error: {e}"
136
+
137
+ # -- Claude CLI (Max Plan) -------------------------------------
138
+
139
+ def _send_claude_cli(self, message: str, history: list[dict],
140
+ system: str) -> str:
141
+ try:
142
+ cmd = ["claude", "--print", "--output-format", "text",
143
+ "--no-session-persistence"]
144
+ if system:
145
+ cmd.extend(["--append-system-prompt", system])
146
+ cmd.append(message)
147
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
148
+ return result.stdout.strip() if result.returncode == 0 else f"CLI error: {result.stderr.strip()}"
149
+ except FileNotFoundError:
150
+ return "Claude CLI not found. Install: https://docs.anthropic.com/claude-code"
151
+ except subprocess.TimeoutExpired:
152
+ return "Claude CLI timeout (300s)."
153
+ except Exception as e:
154
+ return f"CLI error: {e}"
155
+
156
+ def _stream_claude_cli(self, message: str, history: list[dict],
157
+ system: str) -> Iterator[str]:
158
+ """Stream from Claude CLI via subprocess pipe."""
159
+ try:
160
+ cmd = ["claude", "--print", "--output-format", "text",
161
+ "--no-session-persistence"]
162
+ if system:
163
+ cmd.extend(["--append-system-prompt", system])
164
+ cmd.append(message)
165
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
166
+ stderr=subprocess.PIPE, text=True)
167
+ for line in proc.stdout:
168
+ yield line
169
+ proc.wait()
170
+ if proc.returncode != 0:
171
+ err = proc.stderr.read().strip()
172
+ if err:
173
+ yield f"\nCLI error: {err}"
174
+ except FileNotFoundError:
175
+ yield "Claude CLI not found. Install: https://docs.anthropic.com/claude-code"
176
+ except Exception as e:
177
+ yield f"CLI error: {e}"
178
+
179
+ # -- Gemini ----------------------------------------------------
180
+
181
+ def _send_gemini(self, message: str, history: list[dict],
182
+ system: str) -> str:
183
+ try:
184
+ import google.generativeai as genai
185
+ genai.configure()
186
+ model = genai.GenerativeModel(self._model,
187
+ system_instruction=system)
188
+ chat = model.start_chat(history=[
189
+ {"role": h["role"], "parts": [h["content"]]}
190
+ for h in history
191
+ ] if history else [])
192
+ response = chat.send_message(message)
193
+ return response.text
194
+ except ImportError:
195
+ return "Gemini SDK not installed. pip install google-generativeai"
196
+ except Exception as e:
197
+ return f"Gemini error: {e}"
198
+
199
+ # -- Local (Ollama) --------------------------------------------
200
+
201
+ def _send_local(self, message: str, history: list[dict],
202
+ system: str) -> str:
203
+ try:
204
+ result = subprocess.run(
205
+ ["ollama", "run", self._model, message],
206
+ capture_output=True, text=True, timeout=120,
207
+ )
208
+ return result.stdout.strip() if result.returncode == 0 else f"Error: {result.stderr}"
209
+ except FileNotFoundError:
210
+ return "Ollama not installed. https://ollama.ai"
211
+ except Exception as e:
212
+ return f"Local error: {e}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seif-cli
3
- Version: 0.3.3
3
+ Version: 0.3.5
4
4
  Summary: Measure AI output quality, protect sensitive data, watch your AI environment resonate. Quality Gate, Classification, Sentinel & Auto-Healing, SEIF OS.
5
5
  Author: André Cunha Antero de Carvalho
6
6
  License: CC-BY-NC-SA-4.0
@@ -11,6 +11,7 @@ src/seif/analysis/quality_gate.py
11
11
  src/seif/analysis/stance_detector.py
12
12
  src/seif/analysis/transcompiler.py
13
13
  src/seif/bridge/__init__.py
14
+ src/seif/bridge/native_client.py
14
15
  src/seif/bridge/telegram_bot.py
15
16
  src/seif/cli/__init__.py
16
17
  src/seif/cli/__main__.py
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes