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.
Files changed (74) hide show
  1. semanticembed-0.6.0/.github/workflows/publish.yml +51 -0
  2. semanticembed-0.6.0/CHANGELOG.md +86 -0
  3. {semanticembed-0.2.1 → semanticembed-0.6.0}/PKG-INFO +119 -11
  4. {semanticembed-0.2.1 → semanticembed-0.6.0}/README.md +114 -10
  5. semanticembed-0.6.0/examples/sample_pipeline.json +14 -0
  6. {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/03_drift_detection.ipynb +194 -194
  7. {semanticembed-0.2.1 → semanticembed-0.6.0}/pyproject.toml +12 -1
  8. semanticembed-0.6.0/skill/SETUP.md +61 -0
  9. semanticembed-0.6.0/skill/SKILL.md +149 -0
  10. semanticembed-0.6.0/skill/analyze.py +241 -0
  11. {semanticembed-0.2.1 → semanticembed-0.6.0}/src/semanticembed/__init__.py +9 -2
  12. {semanticembed-0.2.1 → semanticembed-0.6.0}/src/semanticembed/client.py +90 -7
  13. semanticembed-0.6.0/src/semanticembed/dedupe.py +116 -0
  14. semanticembed-0.6.0/src/semanticembed/extract.py +1444 -0
  15. semanticembed-0.6.0/src/semanticembed/find_edges.py +282 -0
  16. semanticembed-0.6.0/src/semanticembed/live.py +117 -0
  17. semanticembed-0.6.0/tests/__init__.py +0 -0
  18. semanticembed-0.6.0/tests/fixtures/autogen_app.py +22 -0
  19. semanticembed-0.6.0/tests/fixtures/cdk_app.py +28 -0
  20. semanticembed-0.6.0/tests/fixtures/crewai_app.py +34 -0
  21. semanticembed-0.6.0/tests/fixtures/docker-compose.yml +20 -0
  22. semanticembed-0.6.0/tests/fixtures/langgraph_app.py +47 -0
  23. semanticembed-0.6.0/tests/fixtures/pulumi_app.py +20 -0
  24. semanticembed-0.6.0/tests/fixtures/stack.yaml +35 -0
  25. semanticembed-0.6.0/tests/fixtures/trace_jaeger.json +18 -0
  26. semanticembed-0.6.0/tests/fixtures/trace_otlp.json +48 -0
  27. semanticembed-0.6.0/tests/fixtures/trace_zipkin.json +6 -0
  28. semanticembed-0.6.0/tests/test_client.py +242 -0
  29. semanticembed-0.6.0/tests/test_dedupe.py +122 -0
  30. semanticembed-0.6.0/tests/test_extract.py +141 -0
  31. semanticembed-0.6.0/tests/test_extract_ai.py +185 -0
  32. semanticembed-0.6.0/tests/test_extract_iac.py +147 -0
  33. semanticembed-0.6.0/tests/test_extract_otel.py +91 -0
  34. semanticembed-0.6.0/tests/test_live.py +136 -0
  35. semanticembed-0.6.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.6.0}/.claude/skills/analyze/SKILL.md +0 -0
  42. {semanticembed-0.2.1 → semanticembed-0.6.0}/.gitignore +0 -0
  43. {semanticembed-0.2.1 → semanticembed-0.6.0}/LICENSE +0 -0
  44. {semanticembed-0.2.1 → semanticembed-0.6.0}/docs/api_reference.md +0 -0
  45. {semanticembed-0.2.1 → semanticembed-0.6.0}/docs/dimensions.md +0 -0
  46. {semanticembed-0.2.1 → semanticembed-0.6.0}/docs/getting_started.md +0 -0
  47. {semanticembed-0.2.1 → semanticembed-0.6.0}/docs/input_format.md +0 -0
  48. {semanticembed-0.2.1 → semanticembed-0.6.0}/docs/license_keys.md +0 -0
  49. {semanticembed-0.2.1 → semanticembed-0.6.0}/docs/output_format.md +0 -0
  50. {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/ai_agent_pipeline.json +0 -0
  51. {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/cicd_pipeline.json +0 -0
  52. {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/google_online_boutique.json +0 -0
  53. {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/react/README.md +0 -0
  54. {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/react/RadarChart.tsx +0 -0
  55. {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/react/RiskTable.tsx +0 -0
  56. {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/react/TopologySummary.tsx +0 -0
  57. {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/react/useSemanticEmbed.ts +0 -0
  58. {semanticembed-0.2.1 → semanticembed-0.6.0}/examples/weaveworks_sock_shop.json +0 -0
  59. {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/01_quickstart.ipynb +0 -0
  60. {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/02_dimensions.ipynb +0 -0
  61. {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/04_bring_your_own.ipynb +0 -0
  62. {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/05_ai_agent_pipelines.ipynb +0 -0
  63. {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/06_cicd_pipelines.ipynb +0 -0
  64. {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/07_opentelemetry.ipynb +0 -0
  65. {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/08_qwen_compression.ipynb +0 -0
  66. {semanticembed-0.2.1 → semanticembed-0.6.0}/notebooks/qwen_compression_cookbook.py +0 -0
  67. {semanticembed-0.2.1 → semanticembed-0.6.0/src/semanticembed}/agent/README.md +0 -0
  68. {semanticembed-0.2.1 → semanticembed-0.6.0/src/semanticembed}/agent/__init__.py +0 -0
  69. {semanticembed-0.2.1 → semanticembed-0.6.0/src/semanticembed}/agent/__main__.py +0 -0
  70. {semanticembed-0.2.1 → semanticembed-0.6.0/src/semanticembed}/agent/agent.py +0 -0
  71. {semanticembed-0.2.1 → semanticembed-0.6.0/src/semanticembed}/agent/gemini_agent.py +0 -0
  72. {semanticembed-0.2.1 → semanticembed-0.6.0}/src/semanticembed/exceptions.py +0 -0
  73. {semanticembed-0.2.1 → semanticembed-0.6.0}/src/semanticembed/explain.py +0 -0
  74. {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.2.1
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 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
+
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 claude-agent-sdk
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
- python -m agent
356
+ semanticembed-agent # interactive
357
+ semanticembed-agent --ask "What is my biggest SPOF?"
261
358
 
262
359
  # Gemini agent
263
- pip install google-genai
360
+ pip install 'semanticembed[agent-gemini]'
264
361
  export GOOGLE_API_KEY=...
265
- python -m agent.gemini_agent
266
-
267
- # Single question
268
- python -m agent --ask "What is my biggest SPOF?"
362
+ semanticembed-gemini-agent
269
363
  ```
270
364
 
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.
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 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
+
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 claude-agent-sdk
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
- python -m agent
316
+ semanticembed-agent # interactive
317
+ semanticembed-agent --ask "What is my biggest SPOF?"
225
318
 
226
319
  # Gemini agent
227
- pip install google-genai
320
+ pip install 'semanticembed[agent-gemini]'
228
321
  export GOOGLE_API_KEY=...
229
- python -m agent.gemini_agent
230
-
231
- # Single question
232
- python -m agent --ask "What is my biggest SPOF?"
322
+ semanticembed-gemini-agent
233
323
  ```
234
324
 
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.
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
+ }