semanticembed 0.2.1__tar.gz → 0.6.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.
- semanticembed-0.6.0/.github/workflows/publish.yml +51 -0
- semanticembed-0.6.0/CHANGELOG.md +86 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/PKG-INFO +119 -11
- {semanticembed-0.2.1 → semanticembed-0.6.0}/README.md +114 -10
- semanticembed-0.6.0/examples/sample_pipeline.json +14 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/03_drift_detection.ipynb +194 -194
- {semanticembed-0.2.1 → semanticembed-0.6.0}/pyproject.toml +12 -1
- semanticembed-0.6.0/skill/SETUP.md +61 -0
- semanticembed-0.6.0/skill/SKILL.md +149 -0
- semanticembed-0.6.0/skill/analyze.py +241 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/src/semanticembed/__init__.py +9 -2
- {semanticembed-0.2.1 → semanticembed-0.6.0}/src/semanticembed/client.py +90 -7
- semanticembed-0.6.0/src/semanticembed/dedupe.py +116 -0
- semanticembed-0.6.0/src/semanticembed/extract.py +1444 -0
- semanticembed-0.6.0/src/semanticembed/find_edges.py +282 -0
- semanticembed-0.6.0/src/semanticembed/live.py +117 -0
- semanticembed-0.6.0/tests/__init__.py +0 -0
- semanticembed-0.6.0/tests/fixtures/autogen_app.py +22 -0
- semanticembed-0.6.0/tests/fixtures/cdk_app.py +28 -0
- semanticembed-0.6.0/tests/fixtures/crewai_app.py +34 -0
- semanticembed-0.6.0/tests/fixtures/docker-compose.yml +20 -0
- semanticembed-0.6.0/tests/fixtures/langgraph_app.py +47 -0
- semanticembed-0.6.0/tests/fixtures/pulumi_app.py +20 -0
- semanticembed-0.6.0/tests/fixtures/stack.yaml +35 -0
- semanticembed-0.6.0/tests/fixtures/trace_jaeger.json +18 -0
- semanticembed-0.6.0/tests/fixtures/trace_otlp.json +48 -0
- semanticembed-0.6.0/tests/fixtures/trace_zipkin.json +6 -0
- semanticembed-0.6.0/tests/test_client.py +242 -0
- semanticembed-0.6.0/tests/test_dedupe.py +122 -0
- semanticembed-0.6.0/tests/test_extract.py +141 -0
- semanticembed-0.6.0/tests/test_extract_ai.py +185 -0
- semanticembed-0.6.0/tests/test_extract_iac.py +147 -0
- semanticembed-0.6.0/tests/test_extract_otel.py +91 -0
- semanticembed-0.6.0/tests/test_live.py +136 -0
- semanticembed-0.6.0/tests/test_skill.py +178 -0
- semanticembed-0.2.1/dimensions.md +0 -93
- semanticembed-0.2.1/skill/SETUP.md +0 -59
- semanticembed-0.2.1/skill/SKILL.md +0 -164
- semanticembed-0.2.1/skill/analyze.py +0 -338
- semanticembed-0.2.1/src/semanticembed/extract.py +0 -647
- {semanticembed-0.2.1 → semanticembed-0.6.0}/.claude/skills/analyze/SKILL.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/.gitignore +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/LICENSE +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/docs/api_reference.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/docs/dimensions.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/docs/getting_started.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/docs/input_format.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/docs/license_keys.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/docs/output_format.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/ai_agent_pipeline.json +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/cicd_pipeline.json +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/google_online_boutique.json +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/react/README.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/react/RadarChart.tsx +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/react/RiskTable.tsx +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/react/TopologySummary.tsx +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/react/useSemanticEmbed.ts +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/weaveworks_sock_shop.json +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/01_quickstart.ipynb +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/02_dimensions.ipynb +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/04_bring_your_own.ipynb +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/05_ai_agent_pipelines.ipynb +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/06_cicd_pipelines.ipynb +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/07_opentelemetry.ipynb +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/08_qwen_compression.ipynb +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/qwen_compression_cookbook.py +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0/src/semanticembed}/agent/README.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0/src/semanticembed}/agent/__init__.py +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0/src/semanticembed}/agent/__main__.py +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0/src/semanticembed}/agent/agent.py +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0/src/semanticembed}/agent/gemini_agent.py +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/src/semanticembed/exceptions.py +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/src/semanticembed/explain.py +0 -0
- {semanticembed-0.2.1 → semanticembed-0.6.0}/src/semanticembed/models.py +0 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Triggers on a tag push of the form `v*` — e.g. `git tag v0.6.0 && git push --tags`.
|
|
4
|
+
# Uses PyPI Trusted Publishing (OIDC) so no API token is stored as a GitHub secret.
|
|
5
|
+
# One-time setup: pypi.org -> Account -> Publishing -> Add pending publisher
|
|
6
|
+
# PyPI Project Name: semanticembed
|
|
7
|
+
# Owner: jmurray10
|
|
8
|
+
# Repository: semanticembed-sdk
|
|
9
|
+
# Workflow name: publish.yml
|
|
10
|
+
# Environment: pypi (matches the `environment:` line below)
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
push:
|
|
14
|
+
tags:
|
|
15
|
+
- "v*"
|
|
16
|
+
workflow_dispatch: # allow manual run from the Actions tab
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
build-and-publish:
|
|
20
|
+
name: Build and publish
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
environment: pypi
|
|
23
|
+
permissions:
|
|
24
|
+
id-token: write # required for trusted publishing
|
|
25
|
+
contents: read
|
|
26
|
+
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
|
|
30
|
+
- name: Set up Python
|
|
31
|
+
uses: actions/setup-python@v5
|
|
32
|
+
with:
|
|
33
|
+
python-version: "3.12"
|
|
34
|
+
|
|
35
|
+
- name: Install build tooling
|
|
36
|
+
run: python -m pip install --upgrade build
|
|
37
|
+
|
|
38
|
+
- name: Verify the tag matches the package version
|
|
39
|
+
run: |
|
|
40
|
+
TAG_VERSION="${GITHUB_REF#refs/tags/v}"
|
|
41
|
+
PKG_VERSION=$(python -c "import tomllib; print(tomllib.loads(open('pyproject.toml','rb').read().decode())['project']['version'])")
|
|
42
|
+
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
|
|
43
|
+
echo "Tag version ($TAG_VERSION) does not match pyproject.toml ($PKG_VERSION)" >&2
|
|
44
|
+
exit 1
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
- name: Build sdist + wheel
|
|
48
|
+
run: python -m build
|
|
49
|
+
|
|
50
|
+
- name: Publish to PyPI
|
|
51
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the SemanticEmbed SDK.
|
|
4
|
+
|
|
5
|
+
## 0.6.0 — Infrastructure-as-code parsers
|
|
6
|
+
|
|
7
|
+
- New: `se.extract.from_cloudformation(template)` — parses YAML/JSON templates,
|
|
8
|
+
honors `DependsOn` plus implicit `Ref`/`Fn::GetAtt`/`Fn::Sub` references.
|
|
9
|
+
Custom YAML loader handles short intrinsic tags (`!Ref`, `!GetAtt` etc.).
|
|
10
|
+
- New: `se.extract.from_aws_cdk(file)` — Python CDK construct kwarg references.
|
|
11
|
+
- New: `se.extract.from_pulumi(file)` — Python Pulumi resource kwarg references.
|
|
12
|
+
- `from_directory()` auto-detects all three.
|
|
13
|
+
- TypeScript CDK/Pulumi tracked for a future release.
|
|
14
|
+
|
|
15
|
+
## 0.5.1 — Agent packaging fix
|
|
16
|
+
|
|
17
|
+
- Move `agent/` under `src/semanticembed/agent/` so it actually ships in the
|
|
18
|
+
wheel. Previous installs (0.2.x) declared the agent's dependencies but
|
|
19
|
+
didn't install the agent itself.
|
|
20
|
+
- New console scripts: `semanticembed-agent`, `semanticembed-gemini-agent`.
|
|
21
|
+
Also reachable as `python -m semanticembed.agent`.
|
|
22
|
+
|
|
23
|
+
## 0.5.0 — Live observability connectors
|
|
24
|
+
|
|
25
|
+
- New: `semanticembed.live.from_dynatrace(env_url, api_token)` — pulls
|
|
26
|
+
service-to-service call edges from Smartscape via the Environment API v2.
|
|
27
|
+
Pagination, env-var auth fallback, header-based authentication.
|
|
28
|
+
- Honeycomb / Datadog connectors planned.
|
|
29
|
+
|
|
30
|
+
## 0.4.1 — Service-boundary rollup + encode cache
|
|
31
|
+
|
|
32
|
+
- `from_python_imports(depth=N)` rolls module names up to the first N path
|
|
33
|
+
components. `depth=2` is useful for `services/<svc>/...` monorepos —
|
|
34
|
+
cuts edge-list noise ~10x.
|
|
35
|
+
- `encode(cache=True)` opts into an in-process LRU cache (max 64 entries,
|
|
36
|
+
order-independent edge-set keys). `clear_encode_cache()` for explicit
|
|
37
|
+
eviction. Off by default to preserve current behavior.
|
|
38
|
+
|
|
39
|
+
## 0.4.0 — AI agent framework parsers
|
|
40
|
+
|
|
41
|
+
- New: `from_langgraph(file)` — parses `add_edge`, `add_conditional_edges`
|
|
42
|
+
with explicit `path_map`, `set_entry_point`, `set_finish_point`.
|
|
43
|
+
- New: `from_crewai(file)` — `Task(agent=...)`, `Task(context=[...])`,
|
|
44
|
+
`Crew(manager_agent=...)` patterns.
|
|
45
|
+
- New: `from_autogen(file)` — `GroupChat(agents=[...])` with explicit
|
|
46
|
+
`GroupChatManager` produces a star; without a manager produces a
|
|
47
|
+
fully-connected subgraph. Plus `initiate_chat` edges.
|
|
48
|
+
- Pure AST parsers — no need to `pip install` the framework being analyzed.
|
|
49
|
+
- `from_directory()` auto-detects all three by scanning Python file headers
|
|
50
|
+
for the relevant `import` statements.
|
|
51
|
+
|
|
52
|
+
## 0.3.0 — OpenTelemetry traces + dedupe
|
|
53
|
+
|
|
54
|
+
- New: `from_otel_traces(path)` — auto-detects OTLP / Jaeger / Zipkin JSON.
|
|
55
|
+
Edges emit at the **service** level (intra-service spans roll up).
|
|
56
|
+
- `from_directory()` picks up `traces.json` / `otel.json` / `jaeger.json`
|
|
57
|
+
at the repo root, plus any `*.json` under `traces/`.
|
|
58
|
+
- New: `se.dedupe_edges(edges, normalize, aliases, drop_self_loops)` — for
|
|
59
|
+
blending edges from multiple sources cleanly. Handles the
|
|
60
|
+
`AuthService` / `auth-svc` / `auth_svc` convergence problem. Modes:
|
|
61
|
+
`none` / `snake` / `lower` / `kebab`.
|
|
62
|
+
|
|
63
|
+
## 0.2.3 — Modernized skill (Ollama removed)
|
|
64
|
+
|
|
65
|
+
- The Claude Code skill at `skill/analyze.py` no longer requires Ollama.
|
|
66
|
+
Uses the SDK directly via `se.find_edges()` + `se.encode()`. The parent
|
|
67
|
+
Claude Code agent does any natural-language extraction natively.
|
|
68
|
+
- Single dependency: `pip install 'semanticembed[extract]'`.
|
|
69
|
+
|
|
70
|
+
## 0.2.2 — `find_edges()` + preflight + retry + disclosure
|
|
71
|
+
|
|
72
|
+
- New: `se.find_edges(path, provider, max_nodes)` — programmatic agent
|
|
73
|
+
hook. Tries `extract.from_directory` first (deterministic, no network).
|
|
74
|
+
Falls through to Claude or Gemini only if no infra files are recognized.
|
|
75
|
+
- `encode()` now raises `NodeLimitError` before the HTTP call when no
|
|
76
|
+
license key is set and the graph exceeds the 50-node free tier.
|
|
77
|
+
- `encode()` retries once on `ConnectError` / `ReadTimeout` / 502 / 503 /
|
|
78
|
+
504 with a 0.5s backoff.
|
|
79
|
+
- README adds a "What gets sent where" subsection clarifying that the
|
|
80
|
+
agent paths send file contents to Anthropic / Google APIs.
|
|
81
|
+
|
|
82
|
+
## 0.2.1 and earlier
|
|
83
|
+
|
|
84
|
+
Initial PyPI releases. See git history for the original surface
|
|
85
|
+
(`encode`, `extract.from_docker_compose`, `extract.from_kubernetes`,
|
|
86
|
+
`extract.from_terraform`, `extract.from_python_imports`, `explain`, `ask`).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: semanticembed
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: 6D structural intelligence for directed graphs. Six numbers per node. Sub-millisecond.
|
|
5
5
|
Project-URL: Homepage, https://github.com/jmurray10/semanticembed-sdk
|
|
6
6
|
Project-URL: Documentation, https://github.com/jmurray10/semanticembed-sdk
|
|
@@ -30,6 +30,10 @@ Provides-Extra: all
|
|
|
30
30
|
Requires-Dist: claude-agent-sdk; extra == 'all'
|
|
31
31
|
Requires-Dist: google-genai; extra == 'all'
|
|
32
32
|
Requires-Dist: pyyaml>=6.0; extra == 'all'
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
35
|
+
Requires-Dist: pyyaml>=6.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: respx>=0.20; extra == 'dev'
|
|
33
37
|
Provides-Extra: extract
|
|
34
38
|
Requires-Dist: pyyaml>=6.0; extra == 'extract'
|
|
35
39
|
Description-Content-Type: text/markdown
|
|
@@ -194,12 +198,29 @@ edges = se.extract.from_github_actions(".github/workflows")
|
|
|
194
198
|
# From Terraform
|
|
195
199
|
edges = se.extract.from_terraform("infra/")
|
|
196
200
|
|
|
201
|
+
# From CloudFormation (YAML or JSON)
|
|
202
|
+
edges = se.extract.from_cloudformation("template.yaml")
|
|
203
|
+
|
|
204
|
+
# From AWS CDK (Python)
|
|
205
|
+
edges = se.extract.from_aws_cdk("app.py")
|
|
206
|
+
|
|
207
|
+
# From Pulumi (Python)
|
|
208
|
+
edges = se.extract.from_pulumi("__main__.py")
|
|
209
|
+
|
|
197
210
|
# From Python imports (module dependency graph)
|
|
198
211
|
edges = se.extract.from_python_imports("src/")
|
|
199
212
|
|
|
200
213
|
# From Node.js monorepo (inter-package dependencies)
|
|
201
214
|
edges = se.extract.from_package_json_workspaces(".")
|
|
202
215
|
|
|
216
|
+
# From OpenTelemetry traces (OTLP / Jaeger / Zipkin JSON)
|
|
217
|
+
edges = se.extract.from_otel_traces("traces.json")
|
|
218
|
+
|
|
219
|
+
# From AI agent frameworks (AST-only — no need to install the framework)
|
|
220
|
+
edges = se.extract.from_langgraph("workflow.py") # StateGraph.add_edge / add_conditional_edges / set_entry_point
|
|
221
|
+
edges = se.extract.from_crewai("crew.py") # Task(agent=...) / Task(context=...) / Crew(manager_agent=...)
|
|
222
|
+
edges = se.extract.from_autogen("agents.py") # GroupChat(agents=...) / initiate_chat(...)
|
|
223
|
+
|
|
203
224
|
# Auto-detect everything in a directory
|
|
204
225
|
edges, sources = se.extract.from_directory(".")
|
|
205
226
|
print(f"Found {len(edges)} edges from {sources}")
|
|
@@ -209,7 +230,82 @@ result = se.encode(edges)
|
|
|
209
230
|
print(result.table)
|
|
210
231
|
```
|
|
211
232
|
|
|
212
|
-
Requires `pyyaml` for YAML parsing: `pip install
|
|
233
|
+
Requires `pyyaml` for YAML parsing: `pip install 'semanticembed[extract]'`
|
|
234
|
+
|
|
235
|
+
### Trace ingestion (highest-fidelity edges)
|
|
236
|
+
|
|
237
|
+
Compose / k8s / Terraform describe deployment, not actual call edges. **Real
|
|
238
|
+
runtime traces are the only source with the actual call graph.** v0.3.0 ships
|
|
239
|
+
a deterministic parser for the three common JSON formats:
|
|
240
|
+
|
|
241
|
+
- **OTLP** (OpenTelemetry Collector / SDK exports): `{"resourceSpans": [...]}`
|
|
242
|
+
- **Jaeger** (`jaeger-query` API, `jaeger-cli`): `{"data": [{"spans": [...]}]}`
|
|
243
|
+
- **Zipkin** (Zipkin v2 API): top-level array with `localEndpoint.serviceName`
|
|
244
|
+
|
|
245
|
+
Edges are emitted at the **service level** — same-service spans roll up. Place
|
|
246
|
+
a `traces.json` (or `otel.json` / `jaeger.json` / `zipkin.json`) at your repo
|
|
247
|
+
root and `from_directory()` will pick it up.
|
|
248
|
+
|
|
249
|
+
### Live observability connectors
|
|
250
|
+
|
|
251
|
+
Static analysis is great for repos. For *running* infra, pull traces directly:
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
from semanticembed import live
|
|
255
|
+
|
|
256
|
+
# Dynatrace — Smartscape services + call relationships
|
|
257
|
+
edges = live.from_dynatrace(
|
|
258
|
+
env_url="https://abc12345.live.dynatrace.com",
|
|
259
|
+
api_token=os.environ["DYNATRACE_API_TOKEN"],
|
|
260
|
+
)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Honeycomb and Datadog connectors are tracked for future releases.
|
|
264
|
+
|
|
265
|
+
### AI agent frameworks
|
|
266
|
+
|
|
267
|
+
The three popular Python agent frameworks each have an explicit graph-building
|
|
268
|
+
API. Static AST parsing extracts the actual call graph the framework will run.
|
|
269
|
+
The SDK does **not** import or run the framework — you don't need
|
|
270
|
+
`pip install langgraph` to extract from a LangGraph script.
|
|
271
|
+
|
|
272
|
+
**LangGraph** — `g.add_edge`, `g.add_conditional_edges` (with explicit
|
|
273
|
+
`path_map`), `g.set_entry_point`, `g.set_finish_point`. The sentinels `START`
|
|
274
|
+
and `END` are emitted as literal node names.
|
|
275
|
+
|
|
276
|
+
**CrewAI** — `Task(agent=researcher)` produces `researcher -> task_var`;
|
|
277
|
+
`Task(context=[t1, t2])` produces `t1 -> task_var` / `t2 -> task_var`;
|
|
278
|
+
`Crew(manager_agent=mgr)` adds a `mgr -> agent` fan-out.
|
|
279
|
+
|
|
280
|
+
**AutoGen** — `GroupChat(agents=[a, b, c])` with an explicit
|
|
281
|
+
`GroupChatManager` produces a star (`manager -> a`, `-> b`, `-> c`).
|
|
282
|
+
Without a manager, it's fully connected. `x.initiate_chat(y)` always
|
|
283
|
+
produces `x -> y`.
|
|
284
|
+
|
|
285
|
+
`from_directory()` auto-detects these by scanning Python files for the
|
|
286
|
+
relevant `import` statements and only running the matching parser on those
|
|
287
|
+
files (cheap and accurate vs. walking the whole tree).
|
|
288
|
+
|
|
289
|
+
### Blending sources cleanly
|
|
290
|
+
|
|
291
|
+
Combining traces + compose + Python imports usually produces the same logical
|
|
292
|
+
service under several names (`auth-svc`, `auth_svc`, `AuthService`). Use
|
|
293
|
+
`dedupe_edges` to canonicalize:
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
compose_edges, _ = se.extract.from_directory(".")
|
|
297
|
+
trace_edges = se.extract.from_otel_traces("traces.json")
|
|
298
|
+
|
|
299
|
+
edges = se.dedupe_edges(
|
|
300
|
+
list(compose_edges) + trace_edges,
|
|
301
|
+
normalize="snake", # AuthService -> auth_service
|
|
302
|
+
aliases={"auth_svc": "auth_service"}, # explicit overrides
|
|
303
|
+
)
|
|
304
|
+
result = se.encode(edges)
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Modes: `"none"` (default), `"snake"`, `"lower"`, `"kebab"`. Self-loops are
|
|
308
|
+
dropped by default.
|
|
213
309
|
|
|
214
310
|
---
|
|
215
311
|
|
|
@@ -254,21 +350,33 @@ for node, deltas in changes.items():
|
|
|
254
350
|
An autonomous agent that scans your repo, extracts edges, encodes, and explains results interactively. Choose your LLM backend:
|
|
255
351
|
|
|
256
352
|
```bash
|
|
257
|
-
# Claude agent
|
|
258
|
-
pip install
|
|
353
|
+
# Claude agent (installs the agent code + the Anthropic agent SDK)
|
|
354
|
+
pip install 'semanticembed[agent-claude]'
|
|
259
355
|
export ANTHROPIC_API_KEY=sk-ant-...
|
|
260
|
-
|
|
356
|
+
semanticembed-agent # interactive
|
|
357
|
+
semanticembed-agent --ask "What is my biggest SPOF?"
|
|
261
358
|
|
|
262
359
|
# Gemini agent
|
|
263
|
-
pip install
|
|
360
|
+
pip install 'semanticembed[agent-gemini]'
|
|
264
361
|
export GOOGLE_API_KEY=...
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
# Single question
|
|
268
|
-
python -m agent --ask "What is my biggest SPOF?"
|
|
362
|
+
semanticembed-gemini-agent
|
|
269
363
|
```
|
|
270
364
|
|
|
271
|
-
|
|
365
|
+
Both binaries are also reachable as `python -m semanticembed.agent` /
|
|
366
|
+
`python -m semanticembed.agent.gemini_agent`.
|
|
367
|
+
|
|
368
|
+
The agent has 7 tools: scan, extract (docker-compose, k8s, Python imports), encode, diff, and simulate architecture changes. See [src/semanticembed/agent/README.md](src/semanticembed/agent/README.md) for full docs.
|
|
369
|
+
|
|
370
|
+
### What gets sent where
|
|
371
|
+
|
|
372
|
+
Be explicit about data egress before pointing the agent at private architecture:
|
|
373
|
+
|
|
374
|
+
- **Claude agent** (`python -m agent`): the LLM reads tool outputs as conversation context, so the contents of `docker-compose.yml`, Kubernetes manifests, Terraform `.tf` files, Python source, and `package.json` files in your project go to **Anthropic's API** along with your prompts. Conversation history is governed by Anthropic's data-use policies.
|
|
375
|
+
- **Gemini agent** (`python -m agent.gemini_agent`): same data flow, sent to **Google's API** instead.
|
|
376
|
+
- **Skill** (`skill/analyze.py`): runs **Ollama on your machine**. Raw input never leaves localhost unless you set `SEMBED_OLLAMA_URL` to a remote host.
|
|
377
|
+
- **Cloud `encode()` call**: only the **edge list** (node names, e.g. `["frontend", "auth"]`) goes to the SemanticEmbed Railway endpoint. File contents are never sent.
|
|
378
|
+
|
|
379
|
+
If your topology is sensitive, prefer the skill (local Ollama) or pre-extract edges deterministically with `se.extract.from_directory()` and call `se.encode()` directly — that path sends only the edge list.
|
|
272
380
|
|
|
273
381
|
---
|
|
274
382
|
|
|
@@ -158,12 +158,29 @@ edges = se.extract.from_github_actions(".github/workflows")
|
|
|
158
158
|
# From Terraform
|
|
159
159
|
edges = se.extract.from_terraform("infra/")
|
|
160
160
|
|
|
161
|
+
# From CloudFormation (YAML or JSON)
|
|
162
|
+
edges = se.extract.from_cloudformation("template.yaml")
|
|
163
|
+
|
|
164
|
+
# From AWS CDK (Python)
|
|
165
|
+
edges = se.extract.from_aws_cdk("app.py")
|
|
166
|
+
|
|
167
|
+
# From Pulumi (Python)
|
|
168
|
+
edges = se.extract.from_pulumi("__main__.py")
|
|
169
|
+
|
|
161
170
|
# From Python imports (module dependency graph)
|
|
162
171
|
edges = se.extract.from_python_imports("src/")
|
|
163
172
|
|
|
164
173
|
# From Node.js monorepo (inter-package dependencies)
|
|
165
174
|
edges = se.extract.from_package_json_workspaces(".")
|
|
166
175
|
|
|
176
|
+
# From OpenTelemetry traces (OTLP / Jaeger / Zipkin JSON)
|
|
177
|
+
edges = se.extract.from_otel_traces("traces.json")
|
|
178
|
+
|
|
179
|
+
# From AI agent frameworks (AST-only — no need to install the framework)
|
|
180
|
+
edges = se.extract.from_langgraph("workflow.py") # StateGraph.add_edge / add_conditional_edges / set_entry_point
|
|
181
|
+
edges = se.extract.from_crewai("crew.py") # Task(agent=...) / Task(context=...) / Crew(manager_agent=...)
|
|
182
|
+
edges = se.extract.from_autogen("agents.py") # GroupChat(agents=...) / initiate_chat(...)
|
|
183
|
+
|
|
167
184
|
# Auto-detect everything in a directory
|
|
168
185
|
edges, sources = se.extract.from_directory(".")
|
|
169
186
|
print(f"Found {len(edges)} edges from {sources}")
|
|
@@ -173,7 +190,82 @@ result = se.encode(edges)
|
|
|
173
190
|
print(result.table)
|
|
174
191
|
```
|
|
175
192
|
|
|
176
|
-
Requires `pyyaml` for YAML parsing: `pip install
|
|
193
|
+
Requires `pyyaml` for YAML parsing: `pip install 'semanticembed[extract]'`
|
|
194
|
+
|
|
195
|
+
### Trace ingestion (highest-fidelity edges)
|
|
196
|
+
|
|
197
|
+
Compose / k8s / Terraform describe deployment, not actual call edges. **Real
|
|
198
|
+
runtime traces are the only source with the actual call graph.** v0.3.0 ships
|
|
199
|
+
a deterministic parser for the three common JSON formats:
|
|
200
|
+
|
|
201
|
+
- **OTLP** (OpenTelemetry Collector / SDK exports): `{"resourceSpans": [...]}`
|
|
202
|
+
- **Jaeger** (`jaeger-query` API, `jaeger-cli`): `{"data": [{"spans": [...]}]}`
|
|
203
|
+
- **Zipkin** (Zipkin v2 API): top-level array with `localEndpoint.serviceName`
|
|
204
|
+
|
|
205
|
+
Edges are emitted at the **service level** — same-service spans roll up. Place
|
|
206
|
+
a `traces.json` (or `otel.json` / `jaeger.json` / `zipkin.json`) at your repo
|
|
207
|
+
root and `from_directory()` will pick it up.
|
|
208
|
+
|
|
209
|
+
### Live observability connectors
|
|
210
|
+
|
|
211
|
+
Static analysis is great for repos. For *running* infra, pull traces directly:
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
from semanticembed import live
|
|
215
|
+
|
|
216
|
+
# Dynatrace — Smartscape services + call relationships
|
|
217
|
+
edges = live.from_dynatrace(
|
|
218
|
+
env_url="https://abc12345.live.dynatrace.com",
|
|
219
|
+
api_token=os.environ["DYNATRACE_API_TOKEN"],
|
|
220
|
+
)
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Honeycomb and Datadog connectors are tracked for future releases.
|
|
224
|
+
|
|
225
|
+
### AI agent frameworks
|
|
226
|
+
|
|
227
|
+
The three popular Python agent frameworks each have an explicit graph-building
|
|
228
|
+
API. Static AST parsing extracts the actual call graph the framework will run.
|
|
229
|
+
The SDK does **not** import or run the framework — you don't need
|
|
230
|
+
`pip install langgraph` to extract from a LangGraph script.
|
|
231
|
+
|
|
232
|
+
**LangGraph** — `g.add_edge`, `g.add_conditional_edges` (with explicit
|
|
233
|
+
`path_map`), `g.set_entry_point`, `g.set_finish_point`. The sentinels `START`
|
|
234
|
+
and `END` are emitted as literal node names.
|
|
235
|
+
|
|
236
|
+
**CrewAI** — `Task(agent=researcher)` produces `researcher -> task_var`;
|
|
237
|
+
`Task(context=[t1, t2])` produces `t1 -> task_var` / `t2 -> task_var`;
|
|
238
|
+
`Crew(manager_agent=mgr)` adds a `mgr -> agent` fan-out.
|
|
239
|
+
|
|
240
|
+
**AutoGen** — `GroupChat(agents=[a, b, c])` with an explicit
|
|
241
|
+
`GroupChatManager` produces a star (`manager -> a`, `-> b`, `-> c`).
|
|
242
|
+
Without a manager, it's fully connected. `x.initiate_chat(y)` always
|
|
243
|
+
produces `x -> y`.
|
|
244
|
+
|
|
245
|
+
`from_directory()` auto-detects these by scanning Python files for the
|
|
246
|
+
relevant `import` statements and only running the matching parser on those
|
|
247
|
+
files (cheap and accurate vs. walking the whole tree).
|
|
248
|
+
|
|
249
|
+
### Blending sources cleanly
|
|
250
|
+
|
|
251
|
+
Combining traces + compose + Python imports usually produces the same logical
|
|
252
|
+
service under several names (`auth-svc`, `auth_svc`, `AuthService`). Use
|
|
253
|
+
`dedupe_edges` to canonicalize:
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
compose_edges, _ = se.extract.from_directory(".")
|
|
257
|
+
trace_edges = se.extract.from_otel_traces("traces.json")
|
|
258
|
+
|
|
259
|
+
edges = se.dedupe_edges(
|
|
260
|
+
list(compose_edges) + trace_edges,
|
|
261
|
+
normalize="snake", # AuthService -> auth_service
|
|
262
|
+
aliases={"auth_svc": "auth_service"}, # explicit overrides
|
|
263
|
+
)
|
|
264
|
+
result = se.encode(edges)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Modes: `"none"` (default), `"snake"`, `"lower"`, `"kebab"`. Self-loops are
|
|
268
|
+
dropped by default.
|
|
177
269
|
|
|
178
270
|
---
|
|
179
271
|
|
|
@@ -218,21 +310,33 @@ for node, deltas in changes.items():
|
|
|
218
310
|
An autonomous agent that scans your repo, extracts edges, encodes, and explains results interactively. Choose your LLM backend:
|
|
219
311
|
|
|
220
312
|
```bash
|
|
221
|
-
# Claude agent
|
|
222
|
-
pip install
|
|
313
|
+
# Claude agent (installs the agent code + the Anthropic agent SDK)
|
|
314
|
+
pip install 'semanticembed[agent-claude]'
|
|
223
315
|
export ANTHROPIC_API_KEY=sk-ant-...
|
|
224
|
-
|
|
316
|
+
semanticembed-agent # interactive
|
|
317
|
+
semanticembed-agent --ask "What is my biggest SPOF?"
|
|
225
318
|
|
|
226
319
|
# Gemini agent
|
|
227
|
-
pip install
|
|
320
|
+
pip install 'semanticembed[agent-gemini]'
|
|
228
321
|
export GOOGLE_API_KEY=...
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
# Single question
|
|
232
|
-
python -m agent --ask "What is my biggest SPOF?"
|
|
322
|
+
semanticembed-gemini-agent
|
|
233
323
|
```
|
|
234
324
|
|
|
235
|
-
|
|
325
|
+
Both binaries are also reachable as `python -m semanticembed.agent` /
|
|
326
|
+
`python -m semanticembed.agent.gemini_agent`.
|
|
327
|
+
|
|
328
|
+
The agent has 7 tools: scan, extract (docker-compose, k8s, Python imports), encode, diff, and simulate architecture changes. See [src/semanticembed/agent/README.md](src/semanticembed/agent/README.md) for full docs.
|
|
329
|
+
|
|
330
|
+
### What gets sent where
|
|
331
|
+
|
|
332
|
+
Be explicit about data egress before pointing the agent at private architecture:
|
|
333
|
+
|
|
334
|
+
- **Claude agent** (`python -m agent`): the LLM reads tool outputs as conversation context, so the contents of `docker-compose.yml`, Kubernetes manifests, Terraform `.tf` files, Python source, and `package.json` files in your project go to **Anthropic's API** along with your prompts. Conversation history is governed by Anthropic's data-use policies.
|
|
335
|
+
- **Gemini agent** (`python -m agent.gemini_agent`): same data flow, sent to **Google's API** instead.
|
|
336
|
+
- **Skill** (`skill/analyze.py`): runs **Ollama on your machine**. Raw input never leaves localhost unless you set `SEMBED_OLLAMA_URL` to a remote host.
|
|
337
|
+
- **Cloud `encode()` call**: only the **edge list** (node names, e.g. `["frontend", "auth"]`) goes to the SemanticEmbed Railway endpoint. File contents are never sent.
|
|
338
|
+
|
|
339
|
+
If your topology is sensitive, prefer the skill (local Ollama) or pre-extract edges deterministically with `se.extract.from_directory()` and call `se.encode()` directly — that path sends only the edge list.
|
|
236
340
|
|
|
237
341
|
---
|
|
238
342
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Sample Pipeline",
|
|
3
|
+
"description": "Minimal 7-node pipeline used as the default starter example in notebook 04. Replace the edges with your own architecture to try the SDK on a real graph.",
|
|
4
|
+
"edges": [
|
|
5
|
+
{"source": "frontend", "target": "auth"},
|
|
6
|
+
{"source": "frontend", "target": "catalog"},
|
|
7
|
+
{"source": "frontend", "target": "checkout"},
|
|
8
|
+
{"source": "auth", "target": "db"},
|
|
9
|
+
{"source": "catalog", "target": "db"},
|
|
10
|
+
{"source": "checkout", "target": "payments"},
|
|
11
|
+
{"source": "checkout", "target": "db"},
|
|
12
|
+
{"source": "payments", "target": "stripe"}
|
|
13
|
+
]
|
|
14
|
+
}
|