semanticembed 0.2.1__tar.gz → 0.7.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.7.0/.github/workflows/publish.yml +51 -0
- semanticembed-0.7.0/CHANGELOG.md +101 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/PKG-INFO +132 -11
- {semanticembed-0.2.1 → semanticembed-0.7.0}/README.md +127 -10
- semanticembed-0.7.0/examples/sample_pipeline.json +14 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/03_drift_detection.ipynb +194 -194
- {semanticembed-0.2.1 → semanticembed-0.7.0}/pyproject.toml +12 -1
- semanticembed-0.7.0/skill/SETUP.md +61 -0
- semanticembed-0.7.0/skill/SKILL.md +149 -0
- semanticembed-0.7.0/skill/analyze.py +241 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/src/semanticembed/__init__.py +9 -2
- {semanticembed-0.2.1 → semanticembed-0.7.0}/src/semanticembed/client.py +90 -7
- semanticembed-0.7.0/src/semanticembed/dedupe.py +116 -0
- semanticembed-0.7.0/src/semanticembed/extract.py +1444 -0
- semanticembed-0.7.0/src/semanticembed/find_edges.py +282 -0
- semanticembed-0.7.0/src/semanticembed/live.py +397 -0
- semanticembed-0.7.0/tests/__init__.py +0 -0
- semanticembed-0.7.0/tests/fixtures/autogen_app.py +22 -0
- semanticembed-0.7.0/tests/fixtures/cdk_app.py +28 -0
- semanticembed-0.7.0/tests/fixtures/crewai_app.py +34 -0
- semanticembed-0.7.0/tests/fixtures/docker-compose.yml +20 -0
- semanticembed-0.7.0/tests/fixtures/langgraph_app.py +47 -0
- semanticembed-0.7.0/tests/fixtures/pulumi_app.py +20 -0
- semanticembed-0.7.0/tests/fixtures/stack.yaml +35 -0
- semanticembed-0.7.0/tests/fixtures/trace_jaeger.json +18 -0
- semanticembed-0.7.0/tests/fixtures/trace_otlp.json +48 -0
- semanticembed-0.7.0/tests/fixtures/trace_zipkin.json +6 -0
- semanticembed-0.7.0/tests/test_client.py +242 -0
- semanticembed-0.7.0/tests/test_dedupe.py +122 -0
- semanticembed-0.7.0/tests/test_extract.py +141 -0
- semanticembed-0.7.0/tests/test_extract_ai.py +185 -0
- semanticembed-0.7.0/tests/test_extract_iac.py +147 -0
- semanticembed-0.7.0/tests/test_extract_otel.py +91 -0
- semanticembed-0.7.0/tests/test_live.py +351 -0
- semanticembed-0.7.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.7.0}/.claude/skills/analyze/SKILL.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/.gitignore +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/LICENSE +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/docs/api_reference.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/docs/dimensions.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/docs/getting_started.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/docs/input_format.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/docs/license_keys.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/docs/output_format.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/ai_agent_pipeline.json +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/cicd_pipeline.json +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/google_online_boutique.json +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/react/README.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/react/RadarChart.tsx +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/react/RiskTable.tsx +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/react/TopologySummary.tsx +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/react/useSemanticEmbed.ts +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/weaveworks_sock_shop.json +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/01_quickstart.ipynb +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/02_dimensions.ipynb +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/04_bring_your_own.ipynb +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/05_ai_agent_pipelines.ipynb +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/06_cicd_pipelines.ipynb +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/07_opentelemetry.ipynb +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/08_qwen_compression.ipynb +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/qwen_compression_cookbook.py +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0/src/semanticembed}/agent/README.md +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0/src/semanticembed}/agent/__init__.py +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0/src/semanticembed}/agent/__main__.py +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0/src/semanticembed}/agent/agent.py +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0/src/semanticembed}/agent/gemini_agent.py +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/src/semanticembed/exceptions.py +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.0}/src/semanticembed/explain.py +0 -0
- {semanticembed-0.2.1 → semanticembed-0.7.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,101 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the SemanticEmbed SDK.
|
|
4
|
+
|
|
5
|
+
## 0.7.0 — Honeycomb + Datadog live connectors
|
|
6
|
+
|
|
7
|
+
- New: `semanticembed.live.from_honeycomb(dataset, api_key)` — runs a
|
|
8
|
+
Honeycomb Query API request that breaks down spans by `trace.span_id`,
|
|
9
|
+
`trace.parent_id`, and `service.name`, then derives parent-child
|
|
10
|
+
service edges. Polls the result endpoint until complete.
|
|
11
|
+
Override `api_url` for EU tenants.
|
|
12
|
+
- New: `semanticembed.live.from_datadog(api_key, app_key)` — calls the
|
|
13
|
+
Spans Search API (`POST /api/v2/spans/events/search`), paginates via
|
|
14
|
+
`meta.page.after`, applies the same span-row → service-edge join.
|
|
15
|
+
Optional `env` and `service` filters; `site` override for EU/US3/US5.
|
|
16
|
+
- Both raise clear `ValueError`s when credentials are missing; both fall
|
|
17
|
+
back to canonical env vars (`HONEYCOMB_API_KEY` / `HONEYCOMB_DATASET`,
|
|
18
|
+
`DD_API_KEY` / `DD_APP_KEY`).
|
|
19
|
+
|
|
20
|
+
## 0.6.0 — Infrastructure-as-code parsers
|
|
21
|
+
|
|
22
|
+
- New: `se.extract.from_cloudformation(template)` — parses YAML/JSON templates,
|
|
23
|
+
honors `DependsOn` plus implicit `Ref`/`Fn::GetAtt`/`Fn::Sub` references.
|
|
24
|
+
Custom YAML loader handles short intrinsic tags (`!Ref`, `!GetAtt` etc.).
|
|
25
|
+
- New: `se.extract.from_aws_cdk(file)` — Python CDK construct kwarg references.
|
|
26
|
+
- New: `se.extract.from_pulumi(file)` — Python Pulumi resource kwarg references.
|
|
27
|
+
- `from_directory()` auto-detects all three.
|
|
28
|
+
- TypeScript CDK/Pulumi tracked for a future release.
|
|
29
|
+
|
|
30
|
+
## 0.5.1 — Agent packaging fix
|
|
31
|
+
|
|
32
|
+
- Move `agent/` under `src/semanticembed/agent/` so it actually ships in the
|
|
33
|
+
wheel. Previous installs (0.2.x) declared the agent's dependencies but
|
|
34
|
+
didn't install the agent itself.
|
|
35
|
+
- New console scripts: `semanticembed-agent`, `semanticembed-gemini-agent`.
|
|
36
|
+
Also reachable as `python -m semanticembed.agent`.
|
|
37
|
+
|
|
38
|
+
## 0.5.0 — Live observability connectors
|
|
39
|
+
|
|
40
|
+
- New: `semanticembed.live.from_dynatrace(env_url, api_token)` — pulls
|
|
41
|
+
service-to-service call edges from Smartscape via the Environment API v2.
|
|
42
|
+
Pagination, env-var auth fallback, header-based authentication.
|
|
43
|
+
- Honeycomb / Datadog connectors planned.
|
|
44
|
+
|
|
45
|
+
## 0.4.1 — Service-boundary rollup + encode cache
|
|
46
|
+
|
|
47
|
+
- `from_python_imports(depth=N)` rolls module names up to the first N path
|
|
48
|
+
components. `depth=2` is useful for `services/<svc>/...` monorepos —
|
|
49
|
+
cuts edge-list noise ~10x.
|
|
50
|
+
- `encode(cache=True)` opts into an in-process LRU cache (max 64 entries,
|
|
51
|
+
order-independent edge-set keys). `clear_encode_cache()` for explicit
|
|
52
|
+
eviction. Off by default to preserve current behavior.
|
|
53
|
+
|
|
54
|
+
## 0.4.0 — AI agent framework parsers
|
|
55
|
+
|
|
56
|
+
- New: `from_langgraph(file)` — parses `add_edge`, `add_conditional_edges`
|
|
57
|
+
with explicit `path_map`, `set_entry_point`, `set_finish_point`.
|
|
58
|
+
- New: `from_crewai(file)` — `Task(agent=...)`, `Task(context=[...])`,
|
|
59
|
+
`Crew(manager_agent=...)` patterns.
|
|
60
|
+
- New: `from_autogen(file)` — `GroupChat(agents=[...])` with explicit
|
|
61
|
+
`GroupChatManager` produces a star; without a manager produces a
|
|
62
|
+
fully-connected subgraph. Plus `initiate_chat` edges.
|
|
63
|
+
- Pure AST parsers — no need to `pip install` the framework being analyzed.
|
|
64
|
+
- `from_directory()` auto-detects all three by scanning Python file headers
|
|
65
|
+
for the relevant `import` statements.
|
|
66
|
+
|
|
67
|
+
## 0.3.0 — OpenTelemetry traces + dedupe
|
|
68
|
+
|
|
69
|
+
- New: `from_otel_traces(path)` — auto-detects OTLP / Jaeger / Zipkin JSON.
|
|
70
|
+
Edges emit at the **service** level (intra-service spans roll up).
|
|
71
|
+
- `from_directory()` picks up `traces.json` / `otel.json` / `jaeger.json`
|
|
72
|
+
at the repo root, plus any `*.json` under `traces/`.
|
|
73
|
+
- New: `se.dedupe_edges(edges, normalize, aliases, drop_self_loops)` — for
|
|
74
|
+
blending edges from multiple sources cleanly. Handles the
|
|
75
|
+
`AuthService` / `auth-svc` / `auth_svc` convergence problem. Modes:
|
|
76
|
+
`none` / `snake` / `lower` / `kebab`.
|
|
77
|
+
|
|
78
|
+
## 0.2.3 — Modernized skill (Ollama removed)
|
|
79
|
+
|
|
80
|
+
- The Claude Code skill at `skill/analyze.py` no longer requires Ollama.
|
|
81
|
+
Uses the SDK directly via `se.find_edges()` + `se.encode()`. The parent
|
|
82
|
+
Claude Code agent does any natural-language extraction natively.
|
|
83
|
+
- Single dependency: `pip install 'semanticembed[extract]'`.
|
|
84
|
+
|
|
85
|
+
## 0.2.2 — `find_edges()` + preflight + retry + disclosure
|
|
86
|
+
|
|
87
|
+
- New: `se.find_edges(path, provider, max_nodes)` — programmatic agent
|
|
88
|
+
hook. Tries `extract.from_directory` first (deterministic, no network).
|
|
89
|
+
Falls through to Claude or Gemini only if no infra files are recognized.
|
|
90
|
+
- `encode()` now raises `NodeLimitError` before the HTTP call when no
|
|
91
|
+
license key is set and the graph exceeds the 50-node free tier.
|
|
92
|
+
- `encode()` retries once on `ConnectError` / `ReadTimeout` / 502 / 503 /
|
|
93
|
+
504 with a 0.5s backoff.
|
|
94
|
+
- README adds a "What gets sent where" subsection clarifying that the
|
|
95
|
+
agent paths send file contents to Anthropic / Google APIs.
|
|
96
|
+
|
|
97
|
+
## 0.2.1 and earlier
|
|
98
|
+
|
|
99
|
+
Initial PyPI releases. See git history for the original surface
|
|
100
|
+
(`encode`, `extract.from_docker_compose`, `extract.from_kubernetes`,
|
|
101
|
+
`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.7.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,95 @@ 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
|
+
# Honeycomb — Query API over a dataset
|
|
263
|
+
edges = live.from_honeycomb(
|
|
264
|
+
dataset="my-app-prod",
|
|
265
|
+
api_key=os.environ["HONEYCOMB_API_KEY"],
|
|
266
|
+
lookback_seconds=900,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# Datadog — Spans Search API
|
|
270
|
+
edges = live.from_datadog(
|
|
271
|
+
api_key=os.environ["DD_API_KEY"],
|
|
272
|
+
app_key=os.environ["DD_APP_KEY"],
|
|
273
|
+
env="prod",
|
|
274
|
+
lookback="now-30m",
|
|
275
|
+
)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### AI agent frameworks
|
|
279
|
+
|
|
280
|
+
The three popular Python agent frameworks each have an explicit graph-building
|
|
281
|
+
API. Static AST parsing extracts the actual call graph the framework will run.
|
|
282
|
+
The SDK does **not** import or run the framework — you don't need
|
|
283
|
+
`pip install langgraph` to extract from a LangGraph script.
|
|
284
|
+
|
|
285
|
+
**LangGraph** — `g.add_edge`, `g.add_conditional_edges` (with explicit
|
|
286
|
+
`path_map`), `g.set_entry_point`, `g.set_finish_point`. The sentinels `START`
|
|
287
|
+
and `END` are emitted as literal node names.
|
|
288
|
+
|
|
289
|
+
**CrewAI** — `Task(agent=researcher)` produces `researcher -> task_var`;
|
|
290
|
+
`Task(context=[t1, t2])` produces `t1 -> task_var` / `t2 -> task_var`;
|
|
291
|
+
`Crew(manager_agent=mgr)` adds a `mgr -> agent` fan-out.
|
|
292
|
+
|
|
293
|
+
**AutoGen** — `GroupChat(agents=[a, b, c])` with an explicit
|
|
294
|
+
`GroupChatManager` produces a star (`manager -> a`, `-> b`, `-> c`).
|
|
295
|
+
Without a manager, it's fully connected. `x.initiate_chat(y)` always
|
|
296
|
+
produces `x -> y`.
|
|
297
|
+
|
|
298
|
+
`from_directory()` auto-detects these by scanning Python files for the
|
|
299
|
+
relevant `import` statements and only running the matching parser on those
|
|
300
|
+
files (cheap and accurate vs. walking the whole tree).
|
|
301
|
+
|
|
302
|
+
### Blending sources cleanly
|
|
303
|
+
|
|
304
|
+
Combining traces + compose + Python imports usually produces the same logical
|
|
305
|
+
service under several names (`auth-svc`, `auth_svc`, `AuthService`). Use
|
|
306
|
+
`dedupe_edges` to canonicalize:
|
|
307
|
+
|
|
308
|
+
```python
|
|
309
|
+
compose_edges, _ = se.extract.from_directory(".")
|
|
310
|
+
trace_edges = se.extract.from_otel_traces("traces.json")
|
|
311
|
+
|
|
312
|
+
edges = se.dedupe_edges(
|
|
313
|
+
list(compose_edges) + trace_edges,
|
|
314
|
+
normalize="snake", # AuthService -> auth_service
|
|
315
|
+
aliases={"auth_svc": "auth_service"}, # explicit overrides
|
|
316
|
+
)
|
|
317
|
+
result = se.encode(edges)
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Modes: `"none"` (default), `"snake"`, `"lower"`, `"kebab"`. Self-loops are
|
|
321
|
+
dropped by default.
|
|
213
322
|
|
|
214
323
|
---
|
|
215
324
|
|
|
@@ -254,21 +363,33 @@ for node, deltas in changes.items():
|
|
|
254
363
|
An autonomous agent that scans your repo, extracts edges, encodes, and explains results interactively. Choose your LLM backend:
|
|
255
364
|
|
|
256
365
|
```bash
|
|
257
|
-
# Claude agent
|
|
258
|
-
pip install
|
|
366
|
+
# Claude agent (installs the agent code + the Anthropic agent SDK)
|
|
367
|
+
pip install 'semanticembed[agent-claude]'
|
|
259
368
|
export ANTHROPIC_API_KEY=sk-ant-...
|
|
260
|
-
|
|
369
|
+
semanticembed-agent # interactive
|
|
370
|
+
semanticembed-agent --ask "What is my biggest SPOF?"
|
|
261
371
|
|
|
262
372
|
# Gemini agent
|
|
263
|
-
pip install
|
|
373
|
+
pip install 'semanticembed[agent-gemini]'
|
|
264
374
|
export GOOGLE_API_KEY=...
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
# Single question
|
|
268
|
-
python -m agent --ask "What is my biggest SPOF?"
|
|
375
|
+
semanticembed-gemini-agent
|
|
269
376
|
```
|
|
270
377
|
|
|
271
|
-
|
|
378
|
+
Both binaries are also reachable as `python -m semanticembed.agent` /
|
|
379
|
+
`python -m semanticembed.agent.gemini_agent`.
|
|
380
|
+
|
|
381
|
+
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.
|
|
382
|
+
|
|
383
|
+
### What gets sent where
|
|
384
|
+
|
|
385
|
+
Be explicit about data egress before pointing the agent at private architecture:
|
|
386
|
+
|
|
387
|
+
- **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.
|
|
388
|
+
- **Gemini agent** (`python -m agent.gemini_agent`): same data flow, sent to **Google's API** instead.
|
|
389
|
+
- **Skill** (`skill/analyze.py`): runs **Ollama on your machine**. Raw input never leaves localhost unless you set `SEMBED_OLLAMA_URL` to a remote host.
|
|
390
|
+
- **Cloud `encode()` call**: only the **edge list** (node names, e.g. `["frontend", "auth"]`) goes to the SemanticEmbed Railway endpoint. File contents are never sent.
|
|
391
|
+
|
|
392
|
+
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
393
|
|
|
273
394
|
---
|
|
274
395
|
|
|
@@ -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,95 @@ 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
|
+
# Honeycomb — Query API over a dataset
|
|
223
|
+
edges = live.from_honeycomb(
|
|
224
|
+
dataset="my-app-prod",
|
|
225
|
+
api_key=os.environ["HONEYCOMB_API_KEY"],
|
|
226
|
+
lookback_seconds=900,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# Datadog — Spans Search API
|
|
230
|
+
edges = live.from_datadog(
|
|
231
|
+
api_key=os.environ["DD_API_KEY"],
|
|
232
|
+
app_key=os.environ["DD_APP_KEY"],
|
|
233
|
+
env="prod",
|
|
234
|
+
lookback="now-30m",
|
|
235
|
+
)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### AI agent frameworks
|
|
239
|
+
|
|
240
|
+
The three popular Python agent frameworks each have an explicit graph-building
|
|
241
|
+
API. Static AST parsing extracts the actual call graph the framework will run.
|
|
242
|
+
The SDK does **not** import or run the framework — you don't need
|
|
243
|
+
`pip install langgraph` to extract from a LangGraph script.
|
|
244
|
+
|
|
245
|
+
**LangGraph** — `g.add_edge`, `g.add_conditional_edges` (with explicit
|
|
246
|
+
`path_map`), `g.set_entry_point`, `g.set_finish_point`. The sentinels `START`
|
|
247
|
+
and `END` are emitted as literal node names.
|
|
248
|
+
|
|
249
|
+
**CrewAI** — `Task(agent=researcher)` produces `researcher -> task_var`;
|
|
250
|
+
`Task(context=[t1, t2])` produces `t1 -> task_var` / `t2 -> task_var`;
|
|
251
|
+
`Crew(manager_agent=mgr)` adds a `mgr -> agent` fan-out.
|
|
252
|
+
|
|
253
|
+
**AutoGen** — `GroupChat(agents=[a, b, c])` with an explicit
|
|
254
|
+
`GroupChatManager` produces a star (`manager -> a`, `-> b`, `-> c`).
|
|
255
|
+
Without a manager, it's fully connected. `x.initiate_chat(y)` always
|
|
256
|
+
produces `x -> y`.
|
|
257
|
+
|
|
258
|
+
`from_directory()` auto-detects these by scanning Python files for the
|
|
259
|
+
relevant `import` statements and only running the matching parser on those
|
|
260
|
+
files (cheap and accurate vs. walking the whole tree).
|
|
261
|
+
|
|
262
|
+
### Blending sources cleanly
|
|
263
|
+
|
|
264
|
+
Combining traces + compose + Python imports usually produces the same logical
|
|
265
|
+
service under several names (`auth-svc`, `auth_svc`, `AuthService`). Use
|
|
266
|
+
`dedupe_edges` to canonicalize:
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
compose_edges, _ = se.extract.from_directory(".")
|
|
270
|
+
trace_edges = se.extract.from_otel_traces("traces.json")
|
|
271
|
+
|
|
272
|
+
edges = se.dedupe_edges(
|
|
273
|
+
list(compose_edges) + trace_edges,
|
|
274
|
+
normalize="snake", # AuthService -> auth_service
|
|
275
|
+
aliases={"auth_svc": "auth_service"}, # explicit overrides
|
|
276
|
+
)
|
|
277
|
+
result = se.encode(edges)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Modes: `"none"` (default), `"snake"`, `"lower"`, `"kebab"`. Self-loops are
|
|
281
|
+
dropped by default.
|
|
177
282
|
|
|
178
283
|
---
|
|
179
284
|
|
|
@@ -218,21 +323,33 @@ for node, deltas in changes.items():
|
|
|
218
323
|
An autonomous agent that scans your repo, extracts edges, encodes, and explains results interactively. Choose your LLM backend:
|
|
219
324
|
|
|
220
325
|
```bash
|
|
221
|
-
# Claude agent
|
|
222
|
-
pip install
|
|
326
|
+
# Claude agent (installs the agent code + the Anthropic agent SDK)
|
|
327
|
+
pip install 'semanticembed[agent-claude]'
|
|
223
328
|
export ANTHROPIC_API_KEY=sk-ant-...
|
|
224
|
-
|
|
329
|
+
semanticembed-agent # interactive
|
|
330
|
+
semanticembed-agent --ask "What is my biggest SPOF?"
|
|
225
331
|
|
|
226
332
|
# Gemini agent
|
|
227
|
-
pip install
|
|
333
|
+
pip install 'semanticembed[agent-gemini]'
|
|
228
334
|
export GOOGLE_API_KEY=...
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
# Single question
|
|
232
|
-
python -m agent --ask "What is my biggest SPOF?"
|
|
335
|
+
semanticembed-gemini-agent
|
|
233
336
|
```
|
|
234
337
|
|
|
235
|
-
|
|
338
|
+
Both binaries are also reachable as `python -m semanticembed.agent` /
|
|
339
|
+
`python -m semanticembed.agent.gemini_agent`.
|
|
340
|
+
|
|
341
|
+
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.
|
|
342
|
+
|
|
343
|
+
### What gets sent where
|
|
344
|
+
|
|
345
|
+
Be explicit about data egress before pointing the agent at private architecture:
|
|
346
|
+
|
|
347
|
+
- **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.
|
|
348
|
+
- **Gemini agent** (`python -m agent.gemini_agent`): same data flow, sent to **Google's API** instead.
|
|
349
|
+
- **Skill** (`skill/analyze.py`): runs **Ollama on your machine**. Raw input never leaves localhost unless you set `SEMBED_OLLAMA_URL` to a remote host.
|
|
350
|
+
- **Cloud `encode()` call**: only the **edge list** (node names, e.g. `["frontend", "auth"]`) goes to the SemanticEmbed Railway endpoint. File contents are never sent.
|
|
351
|
+
|
|
352
|
+
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
353
|
|
|
237
354
|
---
|
|
238
355
|
|
|
@@ -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
|
+
}
|