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.
Files changed (74) hide show
  1. semanticembed-0.7.0/.github/workflows/publish.yml +51 -0
  2. semanticembed-0.7.0/CHANGELOG.md +101 -0
  3. {semanticembed-0.2.1 → semanticembed-0.7.0}/PKG-INFO +132 -11
  4. {semanticembed-0.2.1 → semanticembed-0.7.0}/README.md +127 -10
  5. semanticembed-0.7.0/examples/sample_pipeline.json +14 -0
  6. {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/03_drift_detection.ipynb +194 -194
  7. {semanticembed-0.2.1 → semanticembed-0.7.0}/pyproject.toml +12 -1
  8. semanticembed-0.7.0/skill/SETUP.md +61 -0
  9. semanticembed-0.7.0/skill/SKILL.md +149 -0
  10. semanticembed-0.7.0/skill/analyze.py +241 -0
  11. {semanticembed-0.2.1 → semanticembed-0.7.0}/src/semanticembed/__init__.py +9 -2
  12. {semanticembed-0.2.1 → semanticembed-0.7.0}/src/semanticembed/client.py +90 -7
  13. semanticembed-0.7.0/src/semanticembed/dedupe.py +116 -0
  14. semanticembed-0.7.0/src/semanticembed/extract.py +1444 -0
  15. semanticembed-0.7.0/src/semanticembed/find_edges.py +282 -0
  16. semanticembed-0.7.0/src/semanticembed/live.py +397 -0
  17. semanticembed-0.7.0/tests/__init__.py +0 -0
  18. semanticembed-0.7.0/tests/fixtures/autogen_app.py +22 -0
  19. semanticembed-0.7.0/tests/fixtures/cdk_app.py +28 -0
  20. semanticembed-0.7.0/tests/fixtures/crewai_app.py +34 -0
  21. semanticembed-0.7.0/tests/fixtures/docker-compose.yml +20 -0
  22. semanticembed-0.7.0/tests/fixtures/langgraph_app.py +47 -0
  23. semanticembed-0.7.0/tests/fixtures/pulumi_app.py +20 -0
  24. semanticembed-0.7.0/tests/fixtures/stack.yaml +35 -0
  25. semanticembed-0.7.0/tests/fixtures/trace_jaeger.json +18 -0
  26. semanticembed-0.7.0/tests/fixtures/trace_otlp.json +48 -0
  27. semanticembed-0.7.0/tests/fixtures/trace_zipkin.json +6 -0
  28. semanticembed-0.7.0/tests/test_client.py +242 -0
  29. semanticembed-0.7.0/tests/test_dedupe.py +122 -0
  30. semanticembed-0.7.0/tests/test_extract.py +141 -0
  31. semanticembed-0.7.0/tests/test_extract_ai.py +185 -0
  32. semanticembed-0.7.0/tests/test_extract_iac.py +147 -0
  33. semanticembed-0.7.0/tests/test_extract_otel.py +91 -0
  34. semanticembed-0.7.0/tests/test_live.py +351 -0
  35. semanticembed-0.7.0/tests/test_skill.py +178 -0
  36. semanticembed-0.2.1/dimensions.md +0 -93
  37. semanticembed-0.2.1/skill/SETUP.md +0 -59
  38. semanticembed-0.2.1/skill/SKILL.md +0 -164
  39. semanticembed-0.2.1/skill/analyze.py +0 -338
  40. semanticembed-0.2.1/src/semanticembed/extract.py +0 -647
  41. {semanticembed-0.2.1 → semanticembed-0.7.0}/.claude/skills/analyze/SKILL.md +0 -0
  42. {semanticembed-0.2.1 → semanticembed-0.7.0}/.gitignore +0 -0
  43. {semanticembed-0.2.1 → semanticembed-0.7.0}/LICENSE +0 -0
  44. {semanticembed-0.2.1 → semanticembed-0.7.0}/docs/api_reference.md +0 -0
  45. {semanticembed-0.2.1 → semanticembed-0.7.0}/docs/dimensions.md +0 -0
  46. {semanticembed-0.2.1 → semanticembed-0.7.0}/docs/getting_started.md +0 -0
  47. {semanticembed-0.2.1 → semanticembed-0.7.0}/docs/input_format.md +0 -0
  48. {semanticembed-0.2.1 → semanticembed-0.7.0}/docs/license_keys.md +0 -0
  49. {semanticembed-0.2.1 → semanticembed-0.7.0}/docs/output_format.md +0 -0
  50. {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/ai_agent_pipeline.json +0 -0
  51. {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/cicd_pipeline.json +0 -0
  52. {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/google_online_boutique.json +0 -0
  53. {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/react/README.md +0 -0
  54. {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/react/RadarChart.tsx +0 -0
  55. {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/react/RiskTable.tsx +0 -0
  56. {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/react/TopologySummary.tsx +0 -0
  57. {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/react/useSemanticEmbed.ts +0 -0
  58. {semanticembed-0.2.1 → semanticembed-0.7.0}/examples/weaveworks_sock_shop.json +0 -0
  59. {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/01_quickstart.ipynb +0 -0
  60. {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/02_dimensions.ipynb +0 -0
  61. {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/04_bring_your_own.ipynb +0 -0
  62. {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/05_ai_agent_pipelines.ipynb +0 -0
  63. {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/06_cicd_pipelines.ipynb +0 -0
  64. {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/07_opentelemetry.ipynb +0 -0
  65. {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/08_qwen_compression.ipynb +0 -0
  66. {semanticembed-0.2.1 → semanticembed-0.7.0}/notebooks/qwen_compression_cookbook.py +0 -0
  67. {semanticembed-0.2.1 → semanticembed-0.7.0/src/semanticembed}/agent/README.md +0 -0
  68. {semanticembed-0.2.1 → semanticembed-0.7.0/src/semanticembed}/agent/__init__.py +0 -0
  69. {semanticembed-0.2.1 → semanticembed-0.7.0/src/semanticembed}/agent/__main__.py +0 -0
  70. {semanticembed-0.2.1 → semanticembed-0.7.0/src/semanticembed}/agent/agent.py +0 -0
  71. {semanticembed-0.2.1 → semanticembed-0.7.0/src/semanticembed}/agent/gemini_agent.py +0 -0
  72. {semanticembed-0.2.1 → semanticembed-0.7.0}/src/semanticembed/exceptions.py +0 -0
  73. {semanticembed-0.2.1 → semanticembed-0.7.0}/src/semanticembed/explain.py +0 -0
  74. {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.2.1
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 pyyaml`
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 claude-agent-sdk
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
- python -m agent
369
+ semanticembed-agent # interactive
370
+ semanticembed-agent --ask "What is my biggest SPOF?"
261
371
 
262
372
  # Gemini agent
263
- pip install google-genai
373
+ pip install 'semanticembed[agent-gemini]'
264
374
  export GOOGLE_API_KEY=...
265
- python -m agent.gemini_agent
266
-
267
- # Single question
268
- python -m agent --ask "What is my biggest SPOF?"
375
+ semanticembed-gemini-agent
269
376
  ```
270
377
 
271
- The agent has 7 tools: scan, extract (docker-compose, k8s, Python imports), encode, diff, and simulate architecture changes. See [agent/README.md](agent/README.md) for full docs.
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 pyyaml`
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 claude-agent-sdk
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
- python -m agent
329
+ semanticembed-agent # interactive
330
+ semanticembed-agent --ask "What is my biggest SPOF?"
225
331
 
226
332
  # Gemini agent
227
- pip install google-genai
333
+ pip install 'semanticembed[agent-gemini]'
228
334
  export GOOGLE_API_KEY=...
229
- python -m agent.gemini_agent
230
-
231
- # Single question
232
- python -m agent --ask "What is my biggest SPOF?"
335
+ semanticembed-gemini-agent
233
336
  ```
234
337
 
235
- The agent has 7 tools: scan, extract (docker-compose, k8s, Python imports), encode, diff, and simulate architecture changes. See [agent/README.md](agent/README.md) for full docs.
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
+ }