forked from omnigent-ai/omnigent
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpyproject.toml
More file actions
663 lines (617 loc) · 29.9 KB
/
Copy pathpyproject.toml
File metadata and controls
663 lines (617 loc) · 29.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
[build-system]
requires = ["setuptools>=68.0"]
build-backend = "setuptools.build_meta"
[project]
name = "omnigent"
# Keep in sync with omnigent/version.py's VERSION constant.
version = "0.5.0.dev0"
description = "Omnigent: declarative agent authoring and runtime framework"
readme = "README.md"
requires-python = ">=3.12"
authors = [
{ name = "Databricks, Inc." },
]
keywords = ["agents", "llm", "ai", "databricks", "cli"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
dependencies = [
# SDK path-deps, version-locked: the three packages release together at
# one version, so a published `omnigent==X` must resolve the SDK wheels
# built alongside it (release-omnigent.yml verifies these pins match the
# release tag). Local/editable installs still resolve via tool.uv.sources.
"omnigent-client==0.5.0.dev0",
"omnigent-ui-sdk==0.5.0.dev0",
# Merged from omnigent + omnigent.
"pyyaml>=6.0,<7",
"openai>=1.0,<3",
# >=14: the `e2b` extra's SDK (>=2.26) needs rich>=14 (was >=13,<14).
"rich>=14,<15",
"prompt_toolkit>=3.0,<4",
"mcp>=1.0,<2",
"starlette>=1.0.1,<2",
"uvicorn[standard]>=0.30,<1",
# websockets >=15 asyncio client hangs on macOS before emitting any handshake
# bytes; pin below 15 until upstream fixes the regression (see issue #1514).
"websockets>=10.4,<15",
"httpx>=0.27,<1",
"ftfy>=6.0",
# Portable zombie-aware PID liveness checks for the host daemon
# lifecycle (omnigent/host/local_server.py).
"psutil>=5.9,<8",
# CEL (Common Expression Language) for inline policy evaluation.
# Non-Turing-complete, side-effect-free, guaranteed to terminate.
# No wheels for Linux aarch64 or macOS x86_64 (Intel); excluded so
# installation succeeds. The CEL policy module degrades gracefully
# when the library is absent.
'cel-expr-python>=0.1; platform_machine != "aarch64" and (sys_platform != "darwin" or platform_machine != "x86_64")',
# OS keychain for storing model-provider API keys (OSS model
# selection). Used by omnigent/onboarding/secrets.py; falls back to
# a 0600 file when no keyring backend is available (e.g. headless).
"keyring>=24,<26",
# Round-trip (comment/format-preserving) TOML editing of the user's
# ~/.codex/config.toml when removing a Databricks provider
# (omnigent/onboarding/ucode_cleanup.py). stdlib tomllib is read-only.
"tomlkit>=0.12,<1",
# omnigent deps (runtime, spec, server, persistent terminal, etc.).
"alembic>=1.0,<2",
"anyio>=4.0,<5",
"cachetools>=5.0,<7",
# _HostGroup (omnigent/cli.py) rewrites a leading positional server URL
# into `--server` before Click parses it, so it no longer depends on
# Click's internal `protected_args` (made read-only in click 8.2, slated
# for removal in click 9). Upper bound `<10` only guards against an
# as-yet-unreleased click 10 with unknown breaking changes.
"click>=8.0,<10",
"fastapi>=0.100,<1",
# pexpect/pyte back the POSIX tmux/PTY terminal stack, which is disabled
# on Windows (no usable PTY); omnigent never imports them on the core
# path, so skip them there to avoid shipping an unusable dependency.
"pexpect>=4.9,<5; platform_system != 'Windows'",
"pyte>=0.8,<1; platform_system != 'Windows'",
"pydantic>=2.0,<3",
"sqlalchemy>=2.0,<3",
"tiktoken>=0.7,<1",
"opentelemetry-exporter-otlp-proto-grpc>=1.20,<2",
"opentelemetry-exporter-otlp-proto-http>=1.20,<2",
"opentelemetry-instrumentation-fastapi>=0,<1",
"opentelemetry-instrumentation-httpx>=0,<1",
"opentelemetry-instrumentation-sqlalchemy>=0,<1",
# The Claude Code and OpenAI Agents harnesses are part of the
# baseline install — every CUJ command in the README depends on at
# least one of them, so making them optional just forces every user
# to remember `--extra all`. Issue #406.
"claude-agent-sdk>=0.1.62",
"openai-agents>=0.0.17",
"PyJWT[crypto]>=2.8,<3",
"argon2-cffi>=23.1,<24",
# PEP 440 version comparison for the update check / `omni upgrade`
# (omnigent/update_check.py, omnigent/cli.py): compares the installed
# version against the latest PyPI release. Previously only a
# transitive dep; pinned here now that we import it directly.
"packaging>=23,<26",
]
[project.optional-dependencies]
# Retained as no-op aliases so existing `--extra` invocations keep
# working; the underlying packages are now default deps above.
claude-sdk = []
openai-agents = []
# `all` additionally carries databricks-sdk: the SDK moved out of the
# default install (it now lives in the `databricks` extra), but internal
# CI / dev installs use `--extra all` and the test suite, deploy tooling,
# and databricks_mcps servers import it.
all = ["databricks-sdk>=0.56.0,<1"]
# From omnigent.
bedrock = ["boto3>=1.30,<2", "botocore>=1.30,<2"]
# S3-compatible artifact store (AWS S3, Cloudflare R2, MinIO, …). Selected
# with ``OMNIGENT_ARTIFACT_URI=s3://bucket/prefix``; the store imports boto3
# lazily, so only users of this backend need the extra.
s3 = ["boto3>=1.30,<2", "botocore>=1.30,<2"]
vertex = ["google-auth>=2.0,<3"]
# Modal sandbox launcher (`omnigent sandbox --provider modal`). The
# launcher module ships in the base package and imports the SDK lazily,
# so only users of the provider need this extra.
modal = ["modal>=1.0,<2"]
# Daytona sandbox launcher (`omnigent sandbox --provider daytona` and
# server-managed `sandbox.provider: daytona`).
# Same lazy-import posture as the modal extra. Pre-1.0 SDK: pin minor.
daytona = ["daytona>=0.180,<1"]
# Boxlite sandbox launcher: server-managed `sandbox.provider: boxlite`, either
# local embedded micro-VMs (KVM/HVF, no daemon) or a remote `boxlite serve`
# pool. Same lazy-import posture as the modal/daytona extras. Pre-1.0 SDK: pin
# minor.
boxlite = ["boxlite>=0.9.5,<1"]
# CoreWeave Sandbox launcher (`omnigent sandbox --provider cwsandbox` and
# server-managed `sandbox.provider: cwsandbox`).
# Same lazy-import posture as above. Pre-1.0 SDK: pin minor.
cwsandbox = ["cwsandbox>=0.24,<1"]
# E2B sandbox launcher (`omnigent sandbox --provider e2b` and
# server-managed `sandbox.provider: e2b`).
# Same lazy-import posture as above.
e2b = ["e2b>=2.26,<3"]
# NVIDIA OpenShell launcher (`omnigent sandbox --provider openshell`).
# Self-hosted gRPC gateway; the SDK pulls grpcio + protobuf. Same
# lazy-import posture as above. Pre-1.0 SDK: pin minor.
openshell = ["openshell>=0.0.7,<1"]
# On-demand Kubernetes runner Pod launcher (server-managed
# `sandbox.provider: kubernetes`). The launcher module ships in the base
# package and imports the official client lazily, so only users of the
# provider need this extra.
kubernetes = ["kubernetes>=36,<37"]
# The tracing extra is now a no-op shim kept for backwards compatibility.
# Tracing is built on OpenTelemetry directly (opentelemetry-exporter-otlp-*
# are in the default install). `omnigent[tracing]` still works — it just
# no longer pulls in the heavy mlflow dependency.
tracing = []
agents-sdk = [
"openai-agents>=0.1,<1",
"opentelemetry-instrumentation-openai-agents-v2>=0.1",
]
# Google Antigravity SDK harness (`harness: antigravity`). Optional: the
# harness module imports the SDK lazily on first turn, so only users of the
# harness need this extra. Early SDK (v0.1.x) — pin minor.
antigravity = ["google-antigravity>=0.1,<1"]
# GitHub Copilot SDK harness (`harness: copilot`). Optional: the harness module
# imports the SDK lazily on first turn, so only users of the harness need this
# extra. The wheel bundles the Copilot CLI binary it drives (~90MB), which is
# why it is not part of the baseline install. Pin major.
copilot = ["github-copilot-sdk>=1,<2"]
# Cursor SDK harness (`harness: cursor`). Optional like antigravity: the
# harness imports the cursor-sdk lazily on first turn, so only `--harness
# cursor` users need this extra (`omnigent[cursor]`). Was a baseline dependency;
# now opt-in to keep the default install lean.
cursor = ["cursor-sdk>=0.1.7"]
databricks = [
# Floor matches what core code was tested against when the SDK was a
# default dependency (it moved here from `dependencies` above).
"databricks-sdk>=0.56.0,<1",
"opentelemetry-distro>=0,<1",
"psycopg[binary]>=3.1,<4",
# Databricks-specific MCP servers. Not imported by core Omnigent
# code, so it is opt-in here rather than a default dependency.
"databricks-mcp>=0.1.0",
]
dev = [
"pytest>=7.0",
"pytest-asyncio>=0.21",
"pytest-xdist>=3.6",
# Line coverage during the unit-test matrix. Each CI shard writes its
# own data file; the code-coverage workflow combines them. See
# `[tool.coverage.run]` below for the relative-files setting that makes
# cross-runner combine work.
"pytest-cov>=5",
"pytest-playwright>=0.7",
# Visual-regression snapshot plugin for the e2e_ui suite. Provides the
# `assert_snapshot` fixture + `--update-snapshots` flag the UI diff gate
# uses (see tests/e2e_ui/visual/ and .github/workflows/ui-snapshot.yml).
# Requires pytest-playwright>=0.7 (hence the floor bump above).
"pytest-playwright-visual-snapshot>=0.5.1",
# Hash-partition the test suite across N shards for the nightly
# matrix. Pinned to a known-stable older release. Used by e2e.yml;
# the e2e-ui suite uses its own round-robin `--splits`/`--group`
# split (see tests/e2e_ui/conftest.py), so no extra dep there.
"pytest-shard==0.1.2",
# Per-test wallclock cap. Without this, a single hung test (e.g.
# a pexpect REPL test waiting for a terminal pattern that never
# arrives) blocks the whole pytest session until the workflow's
# step-level timeout-minutes fires, killing the worker without
# producing a junit XML or per-test traceback. With --timeout=N,
# pytest-timeout fails the offending test with a clear traceback
# and lets the rest of the suite continue. Pinned exactly to a
# known-good release for consistency with pytest-shard above.
"pytest-timeout==2.4.0",
# Retry LLM-driven inner tests up to 2 extra times on codex; see
# ``pytest_collection_modifyitems`` in ``tests/inner/conftest.py``.
# Pinned exactly to a known-stable release for consistency with
# pytest-shard / pytest-timeout above. 16.2+ is required under
# pytest 9: 14.0 predates pytest 9 and corrupts fixture-finalizer
# state around reruns (stale FixtureDef._finalizers / SetupState
# surgery, fixed upstream in 16.2), which leaked one test's
# monkeypatch of ``sessions._get_runner_client`` into later tests
# on the same xdist worker and flaked the WS-tunnel suite (the
# "Runner stream relay exited before becoming ready" 503s).
"pytest-rerunfailures==16.3",
"filelock>=3.0",
"ruff>=0.6",
"mypy>=1.10",
"pre-commit>=3.5",
"types-PyYAML>=6.0",
"filelock>=3.0",
# gitwildmatch glob matching for repo tooling manifests. Declared
# explicitly rather than relied on transitively (via mypy).
"pathspec>=0.12",
# Cloud-service mocks for the artifact-store / Cloudflare integration
# tests: moto is the standard S3-compatible mock (used for R2), respx is
# the standard HTTPX mock (used to back the Cloudflare D1 REST API with an
# in-memory SQLite), and boto3 backs the S3 artifact store under test.
"moto[s3]>=5,<6",
"respx>=0.21,<1",
"boto3>=1.30,<2",
# The Cloudflare D1 SQLAlchemy dialect, exercised by the D1 FTS integration
# test (tests/db/test_d1_fts_dialect.py) via respx + sqlite3.
"sqlalchemy-cloudflare-d1==0.3.10",
]
[project.scripts]
# Unified entry point. `omnigent` is the click-based CLI that absorbs
# the legacy argparse commands from `omnigent/inner/cli.py` plus the
# omnigent `ap` subcommands. See designs/UNIFICATION.md § CLI
# absorption. `omni` is installed as a short alias — both resolve to
# the same entry point. (No former-name aliases: only `omnigent` and
# `omni` ship, internal and OSS alike.)
omnigent = "omnigent.cli:main"
omni = "omnigent.cli:main"
# uv path-deps for the sibling SDK packages (renamed in Stage 3).
[tool.uv.sources]
omnigent-client = { path = "sdks/python-client", editable = true }
omnigent-ui-sdk = { path = "sdks/ui", editable = true }
[tool.setuptools]
include-package-data = true
[tool.setuptools.packages.find]
where = ["."]
# Discovers the `omnigent` package (including `omnigent.inner.*` and
# all omnigent subpackages absorbed during unification).
include = ["omnigent*"]
[tool.setuptools.package-data]
"omnigent.db" = ["alembic.ini"]
"omnigent.db.migrations" = ["script.py.mako"]
"omnigent.resources.examples" = ["*.yaml", "*.sh", "debby/**/*", "polly/**/*"]
"omnigent.resources.pi_native" = ["*.js"]
# include UI assets in build, plus the static API-only landing page served
# at "/" when no web UI bundle is present
"omnigent.server" = ["static/web-ui/**/*", "static/api_only_landing.html"]
[tool.pytest.ini_options]
# auto mode: async tests without @pytest.mark.asyncio() are auto-
# collected as asyncio. Inner (omnigent-legacy) tests rely on
# this. SDK tests (tests/frontends/sdk/) use explicit markers;
# both work under auto.
asyncio_mode = "auto"
testpaths = ["tests"]
# tests/e2e, tests/e2e_ui, and tests/e2e_live all require real LLM
# credentials and/or extra setup (Databricks profile; npm build +
# Playwright browsers for the UI suite; a deployed Databricks App URL
# for the live suite). Opt-in via explicit pytest invocation.
addopts = "--ignore=tests/e2e --ignore=tests/e2e_ui --ignore=tests/e2e_live --ignore=tests/integration"
# Default per-test wallclock cap. Catches infinite hangs in any
# suite (unit, inner, integration, e2e) without bothering tests
# that actually finish. The longest legitimate integration tests
# are well under 5 min; anything longer than that is almost
# certainly a hang and should surface a traceback rather than
# stall the worker. CLI overrides still work: e2e workflows pass
# a stricter --timeout=180; individual tests can opt out via
# ``@pytest.mark.timeout(N)``. ``thread`` method is more reliable
# than the default ``signal`` method when tests fork subprocesses
# (SIGALRM doesn't reach blocked-on-pty children) — see
# pytest-timeout README.
timeout = 300
timeout_method = "thread"
# SDK tests under ``tests/frontends/sdk/`` import the
# ``omnigent_client`` and ``omnigent_ui_sdk`` packages that
# live in ``sdks/python-client/`` and ``sdks/ui/`` respectively.
# Without this pythonpath the imports fail at collection time
# with ``ModuleNotFoundError: No module named 'omnigent_client'``
# unless the SDKs are installed editable into the venv. Listing
# both relative paths here keeps the test suite self-contained.
pythonpath = [".", "sdks/python-client", "sdks/ui"]
markers = [
"live: end-to-end tests requiring real LLM API key (run with --llm-api-key)",
"live_app: end-to-end tests requiring a deployed Databricks App server",
"databricks: requires the `databricks` extra (psycopg / databricks-sdk). Deselected on the standard lanes via -m 'not databricks'; runs only in the Pytest (databricks) lane, which installs the extra. Use for tests that build a postgresql+psycopg engine or call the Databricks SDK.",
"model(name, ...): one arg pins this test to that model overriding the suite-wide --model CLI default; multiple args fan the test out across all listed models via indirect parametrize.",
"model_pinned: opt out of tests/_model_pools.py spreading and retry rotation; literal model strings are used as written.",
"flaky(reruns=2, reruns_delay=0): rerun a nondeterministic test on failure (pytest-rerunfailures). Use for tests flaky due to timing/scheduling races (e.g. PTY drain timing), NOT real-LLM nondeterminism — those use llm_flaky, which rotates models per attempt.",
"llm_flaky(reruns=2, reruns_delay=1): rerun a nondeterministic real-LLM test on failure, rotating to a different model each attempt (tests/_model_pools.py). Never apply to heavy e2e tests that can hit the CI --timeout=180 cap (rerun + loadscope can crash the shard).",
"in_process_sessions: run a server integration test with the legacy in-process sessions path instead of sessions-native runner dispatch.",
"nightly: runs only in the scheduled/dispatch pass of its suite; PR and push runs exclude it via -m 'not nightly'. Remove the marker to promote a burned-in test to the PR gate.",
"min_server_version(version): skip this test when the live server under test is older than `version` (PEP 440 release-tuple comparison, so a `.devN` of X satisfies X). Used by the server-version backwards-compat CI (docs/SERVER_VERSION_COMPAT_CI.md); inert in normal runs where the server is current.",
"min_runner_version(version): skip this test when the pinned runner/host under test is older than `version` (PEP 440 release-tuple comparison, so a `.devN` of X satisfies X). Used by the runner/host backwards-compat CI (Config 2); inert in normal runs (no OMNIGENT_COMPAT_RUNNER_VERSION set).",
"mock_only: tests/integration test that only works in mock-LLM mode (no --llm-api-key). Skipped by tests/integration/conftest.py when a real --llm-api-key is supplied (the real-LLM Integration jobs). Use for tests whose mock LLM is scripted with a fixed tool-call sequence — a real LLM cannot reproduce the scripted markers.",
"visual: UI diff visual-regression snapshot (pytest-playwright-visual-snapshot). Runs only in the pinned-runner gate (.github/workflows/ui-snapshot.yml); the main e2e_ui suite excludes it via -m 'not visual' since it runs on the unpinned ubuntu-latest.",
"posix_only: test relies on POSIX-only behaviour (fork, PTY, tmux, Unix sockets, signals); auto-skipped on Windows by tests/conftest.py.",
"windows_only: test relies on Windows-only behaviour (Job Objects, cmd.exe); auto-skipped on POSIX by tests/conftest.py.",
]
# Visual snapshot baselines are committed here, keyed per-OS by the plugin
# (e.g. test_empty_landing_matches_baseline[linux].png). Failure artifacts
# (actual/expected/diff PNGs) land in the gitignored sibling dir. Both are
# relative to the repo root, which is where pytest is always invoked.
playwright_visual_snapshots_path = "tests/e2e_ui/visual/snapshots"
playwright_visual_snapshot_failures_path = "tests/e2e_ui/visual/snapshot_failures"
[tool.coverage.run]
# Measure the shipped package only; tests live under tests/.
source = ["omnigent"]
# Each CI shard sets COVERAGE_FILE=artifacts/.coverage.<group> and writes
# one data file (pytest-cov already combines the xdist workers within a
# shard). `relative_files` stores paths relative to the repo root so the
# code-coverage workflow can `coverage combine` data files produced on
# separate sharded runners with differing absolute checkout paths.
relative_files = true
[tool.coverage.report]
show_missing = true
# Report-only for now: no `fail_under`, so coverage never fails a build.
# The code-coverage workflow reports against a plain `git checkout` that
# has no generated files (e.g. omnigent/_build_info.py, written during
# `uv sync` in the CI shards). Without this, `coverage report`/`xml` exit
# 1 on "No source for code" and fail the step under `set -e`; ignore those
# unreadable-source files instead.
ignore_errors = true
[tool.ruff]
line-length = 99
# NOTE: kept at py310 even though requires-python is >=3.12. Bumping this to
# py312 makes ruff's UP041 rewrite `asyncio.TimeoutError` -> `TimeoutError`
# (aliased since 3.11) across the whole codebase — a separate, out-of-scope
# migration that belongs in its own cleanup PR.
target-version = "py310"
# Vendored upstream code — not ours to restyle.
# - ``omnigent/inner/databricks_mcps/google``: Google MCP server source.
# - ``assistant-ui/``: assistant-ui.com React/Python suite imported as a
# vendored snapshot; tracked upstream, no in-tree edits.
extend-exclude = [
"omnigent/inner/databricks_mcps/google",
"assistant-ui",
]
[tool.ruff.lint]
# ``E``/``F`` — pycodestyle + pyflakes baseline
# ``I`` — import sorting
# ``UP`` — pyupgrade (modernize syntax)
# ``ARG`` — unused function arguments
# ``BLE`` — blind ``except Exception``
# ``B`` — flake8-bugbear (latent bugs, e.g. mutable defaults,
# unbound loop variables in closures, raise-from)
# ``SIM`` — flake8-simplify (reduce unnecessary complexity)
# ``RET`` — flake8-return (consistent return statements)
# ``C4`` — flake8-comprehensions (cleaner list/dict/set comprehensions)
# ``PIE`` — flake8-pie (misc correctness + style)
# ``RUF`` — ruff-specific (sorted ``__all__``, dangling asyncio
# tasks, unused noqa, ...)
select = ["E", "F", "I", "UP", "ARG", "BLE", "B", "SIM", "RET", "C4", "PIE", "RUF"]
ignore = [
# ARG003 (unused class-method args): too noisy on ABC-subclass signatures
# where the arg is required by the interface contract.
"ARG003",
# RUF001/002/003 ambiguous-unicode: the repo uses ``—`` / ``–`` / ``×`` /
# ``╱`` in docstrings and CLI UI strings intentionally. The rule was
# designed for source strings that could be confused with ASCII and
# throws off on legitimate typographic characters in prose.
"RUF001",
"RUF002",
"RUF003",
# SIM102/103/108: style-only transforms (collapse nested if / return
# condition directly / rewrite if-else as ternary). Often make code
# harder to read when the conditions are non-trivial. Kept available
# as manual refactoring hints but not enforced.
"SIM102",
"SIM103",
"SIM108",
]
[tool.ruff.lint.isort]
# Classify first-party imports by NAME, not by file existence. Ruff's
# default detection resolves each module against the filesystem, so an
# import of an optional submodule absent from a given checkout (e.g.
# omnigent.onboarding.internal_beta) would be reclassified as third-party
# there, demanding a different import order per checkout. Pinning the
# package name keeps one canonical order everywhere.
known-first-party = ["omnigent"]
[tool.ruff.lint.per-file-ignores]
# Tests legitimately accept parameters they don't use (pytest fixtures,
# mock callback signatures) and intercept broad exceptions to assert on
# error paths. Cleaning these up is a separate cleanup pass.
#
# RUF012 (mutable default class attribute): test fake classes and
# Textual/pytest harnesses routinely declare class-level
# ``BINDINGS = [...]`` / ``inputSchema: dict = {...}`` that are never
# mutated — the rule's production-shared-state concern doesn't apply.
#
# B007 (loop var unused): occasional ``for i, x in enumerate(...)`` in
# tests where only ``x`` matters; style not worth policing.
#
# B008 (function call in default): pytest / factory_boy idioms.
#
# SIM117 (combined with): already-legible nested ``with`` in test
# fixtures that would be harder to read if merged.
"tests/**/*.py" = [
"ARG001", "ARG002", "ARG004", "ARG005", "BLE001",
"RUF012", "B007", "B008", "SIM117",
]
# Example scripts predate the rule and are exempted until they graduate.
"tests/resources/examples/**/*.py" = [
"ARG001", "ARG002", "ARG004", "ARG005", "BLE001",
"RUF012", "B007", "B008", "SIM117",
]
# Graduation backlog for code migrated from omnigent during the
# unification. Pre-unification Omnigent' ruff config only selected
# ``E, F, I, UP`` — none of the ARG/BLE/B/SIM/... rules — so these
# files have a backlog of unused-arg, blind-except, and bugbear issues
# that were invisible before. Graduate each subpath out of this list
# as it's cleaned up.
"omnigent/llms/**/*.py" = [
"ARG001", "ARG002", "BLE001", "B008", "RUF012",
]
"omnigent/onboarding/**/*.py" = [
"ARG001", "ARG002", "BLE001", "B008", "RUF012",
]
"omnigent/runtime/**/*.py" = [
"ARG001", "ARG002", "BLE001", "B008", "RUF012",
]
"omnigent/terminals/**/*.py" = [
"ARG001", "ARG002", "BLE001", "B008", "RUF012",
]
"omnigent/tools/**/*.py" = [
"ARG001", "ARG002", "BLE001", "B008", "RUF012",
]
# The native Antigravity (agy) transcript forwarder / supervisor is a
# long-lived boundary loop that must restart on ANY non-cancellation
# Exception (a crash there silently stops the web transcript mirror), so its
# top-level catch is a deliberate, documented boundary catch — BLE001 is
# waived for these top-level antigravity_native modules (see the inline
# comments at each catch site).
"omnigent/antigravity_native*.py" = ["BLE001"]
"sdks/**/*.py" = [
"ARG001", "ARG002", "BLE001", "B008", "RUF012",
]
[tool.mypy]
python_version = "3.12"
strict = true
# Ban explicit `Any` in annotations. `Any` defeats static typing; prefer
# real types, Protocols, TypedDicts, or `object` with runtime checks. When
# a genuine boundary needs it (opaque JSON shapes, foreign SDKs) annotate
# with `# type: ignore[explicit-any]` *and a comment explaining why*.
disallow_any_explicit = true
# Vendored Google MCP server code — upstream code, not ours to type.
# setup.py — build-time scaffolding that imports the (untyped) setuptools
# build_py and subclasses it; not application code, so excluded from mypy
# rather than pulling in types-setuptools just for one shim file.
exclude = ["^omnigent/inner/databricks_mcps/google/", "^setup\\.py$"]
# Incremental rollout: load imported modules for type info but don't
# report their errors. Remove once every omnigent/ module is clean.
follow_imports = "silent"
[[tool.mypy.overrides]]
module = "mcp.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "databricks.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "databricks_mcp.*"
ignore_missing_imports = true
# psycopg is an optional dep (only declared in the ``databricks``
# extra, used by ``deploy/databricks/grant_sp_perms.py``). Previously installed
# transitively via ``dbos``; after the DBOS removal, ``uv sync
# --extra dev`` no longer pulls it, so the lint workflow can't
# import it. Treat the missing module as silent — runtime use still
# requires installing the ``databricks`` extra.
[[tool.mypy.overrides]]
module = "psycopg.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "openai.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "claude_agent_sdk.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "agents.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "ftfy"
ignore_missing_imports = true
# modal is an optional dep (the `modal` extra); the sandbox launcher
# imports it lazily so the base install never needs it.
[[tool.mypy.overrides]]
module = "modal.*"
ignore_missing_imports = true
# daytona is an optional dep (the `daytona` extra); same lazy-import
# posture as modal above.
[[tool.mypy.overrides]]
module = "daytona.*"
ignore_missing_imports = true
# cwsandbox is an optional dep (the `cwsandbox` extra); same lazy-import
# posture as modal above.
[[tool.mypy.overrides]]
module = "cwsandbox.*"
ignore_missing_imports = true
# e2b is an optional dep (the `e2b` extra); same lazy-import posture as
# modal above.
[[tool.mypy.overrides]]
module = "e2b.*"
ignore_missing_imports = true
# openshell is an optional dep (the `openshell` extra); same lazy-import
# posture as modal above. grpc rides in via the openshell SDK and is not
# a base dependency, so its imports are unresolved in the default env too.
[[tool.mypy.overrides]]
module = "openshell.*"
ignore_missing_imports = true
# boxlite is an optional dep (the `boxlite` extra); same lazy-import posture
# as modal above.
[[tool.mypy.overrides]]
module = "boxlite.*"
ignore_missing_imports = true
# kubernetes is an optional dep (the `kubernetes` extra); same lazy-import
# posture as modal above. urllib3 rides in via the kubernetes client.
[[tool.mypy.overrides]]
module = "kubernetes.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "grpc.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "prompt_toolkit.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "yaml"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "pexpect"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "cachetools"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "httpx"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "boto3"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "botocore.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "google.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "opentelemetry.instrumentation.*"
ignore_missing_imports = true
# The Modal deploy glue (deploy/modal/modal_app.py) decorates with the
# optional `modal` extra, which the lint env doesn't install — under the
# modal.* ignore-missing-imports override above its decorators type as
# Any and trip disallow_untyped_decorators. Relax just that flag.
[[tool.mypy.overrides]]
module = "modal_app"
disallow_untyped_decorators = false
# omnigent-derived repl code has its own relaxed typing config.
[[tool.mypy.overrides]]
module = "omnigent.repl.*"
disallow_untyped_defs = false
disallow_untyped_decorators = false
warn_return_any = false
check_untyped_defs = false
# omnigent-derived code that pre-unification omnigent never
# subjected to ``disallow_any_explicit`` (its pyproject.toml didn't
# set that flag). Keep the rest of strict mypy on — just relax the
# explicit-Any ban until these modules can be graduated.
[[tool.mypy.overrides]]
module = [
"omnigent.runtime.*",
"omnigent.server.*",
"omnigent.tools.*",
"omnigent.entities.*",
"omnigent.stores.*",
"omnigent.db.*",
"omnigent.llms.*",
"omnigent.onboarding.*",
"omnigent.terminals.*",
"omnigent.client_tools.*",
"omnigent.spec.omnigent",
"omnigent.spec._omnigent_compat",
"dump_openapi",
]
disallow_any_explicit = false
[[tool.mypy.overrides]]
module = "omnigent_ui_sdk.*"
ignore_missing_imports = true
follow_imports = "skip"
# SDK shim aliases — ignore their own import resolution issues. These
# are re-exports of omnigent_client/omnigent_ui_sdk (post-Stage-3)
# via sys.modules aliasing.
[[tool.mypy.overrides]]
module = "omnigent_client.*"
ignore_missing_imports = true
disallow_any_explicit = false
# Tests in the mypy allowlist predate the stricter omnigent
# Any rule.
[[tool.mypy.overrides]]
module = "tests.*"
disallow_any_explicit = false