Implementation history — for Temporal AI Partner technical review - #6
Draft
tyler5673 wants to merge 37 commits into
Draft
Implementation history — for Temporal AI Partner technical review#6tyler5673 wants to merge 37 commits into
tyler5673 wants to merge 37 commits into
Conversation
- export set_config from __init__ for programmatic config override - remove wrapper functions in test_activities, import activities directly - remove non-asserting test_contents_activity_default_markdown - remove duplicate test_you_activities_matches_plugin_activities - remove redundant @pytest.mark.asyncio (asyncio_mode=auto handles it) 34 tests passing, ruff + mypy clean.
Replace manual os.environ try/finally with monkeypatch fixture for automatic cleanup. Matches the pattern already used in test_activities.
- README with quickstart (plugin + manual paths), activity table, error handling, security notes, and example usage - CONTRIBUTING with dev setup, checks, commit conventions, and guidance for adding new activities - add readme = README.md to pyproject.toml
- test_global_override_wins_over_env: set_config() programmatic override beats YDC_API_KEY env via the production _cfg() read path - test_typed_internal_server_errors_passthrough: Search/Research/Contents InternalServerError fall through to retryable (they extend YouError directly, not YouDefaultError) 38 tests passing, ruff + mypy clean.
Previously to_temporal_error was only tested in isolation via test_errors.py. No test verified that an SDK error raised inside an activity's try/except block was correctly caught and mapped. Added 4 tests covering: - search activity: SDK auth error (401) -> YouAuthError non-retryable - search activity: SDK quota error (402) -> YouQuotaExhausted non-retryable - research activity: SDK auth error (401) -> YouAuthError non-retryable - contents activity: SDK auth error (401) -> YouAuthError non-retryable Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…sts, codeowners Cross-referenced with temporalio/sdk-python, langchain-youdotcom, and n8n-nodes-youdotcom to align this repo with established OSS patterns. Changes: - pyproject.toml: add [project.urls], classifiers, keywords (matches temporalio/sdk-python and langchain-youdotcom) - CHANGELOG.md: Keep-a-Changelog format (matches temporalio/sdk-python) - publish.yml: upgrade from tag-triggered to workflow_dispatch with version bumping, prerelease support, and GitHub Release creation (matches langchain-youdotcom and n8n-nodes-youdotcom) - ci.yml: add workflow_dispatch integration test job with YDC_API_KEY secret and Temporal CLI install (matches langchain-youdotcom) - .github/CODEOWNERS: auto-assign dx team for PR review (matches dx-toolkit) Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Add call_args assertions proving every SearchInput/ResearchInput/ContentsInput field reaches the correct SDK method with the right enum conversion, plus client-construction tests for retry_config/timeout_ms/server_url kwargs. Move the end-to-end demo into the repo as tests/test_integration.py, gated by an integration marker and skipped without YDC_API_KEY, so the CI integration job has real tests to run. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
The integration tests self-skip without YDC_API_KEY and we're not adding a key to a public repo, so the job was dead weight. The integration test file stays for local runs (pytest -m integration). Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
https://you.com/platform/api-keys is a sign-in wall; https://you.com/platform is the public API platform page linked from docs and pricing. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
- Replace sign-in-wall https://you.com/platform/api-keys with public https://you.com/platform in README and QUOTA_CTA. - Soften quota message to "Check your usage and plan options". Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…dpoint, fix stale docs - examples/run_worker.py: remove unnecessary YouPlugin() from Client.connect() (SimplePlugin only provides activities and workflow runner, client doesn't need it) - _errors.py: map 422 from YouDefaultError to YouValidationError non-retryable (previously only the typed UnprocessableEntityError from research was caught; search/contents 422 would have passthrough as retryable) - tests/test_errors.py: add test for 422 via YouDefaultError - CHANGELOG.md: fix test count (42 after adding the new 422 test) - CONTRIBUTING.md: clarify integration tests are not run in CI, only locally Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Replace the SDK's default YDCUserAgentOverrideHook with a custom _TemporalUserAgentHook that sets User-Agent: youdotcom-temporal/0.1.0 on every request. This matches the pattern used by langchain-youdotcom and n8n-nodes-youdotcom for integration usage tracking. The SDK's hook unconditionally overrides User-Agent on every request, so swapping the hook after SDK construction is the reliable way to set a custom user-agent. Tests cover the hook behavior, header setting, and hook replacement.
Remove the _TemporalUserAgentHook and _patch_user_agent workaround. Instead, set client.sdk_configuration.user_agent directly after SDK construction. This is the proper approach that will work once the SDK's YDCUserAgentOverrideHook is fixed to respect custom user_agent values (tracked in sdk-spec-drift-audit memory log). Also make __version__ dynamic via importlib.metadata instead of hardcoding 0.1.0. The user-agent string now derives from the installed package version automatically.
Fix error class names (SearchUnauthorizedError → UnauthorizedResponseError, etc.), add missing search/contents/research params, add three new activities (Answer, Finance Research, Background Research), bump youdotcom to >=3.0.0,<4. Changes: - _errors.py: replace removed search-specific error classes with generic ones (UnauthorizedResponseError, ForbiddenResponseError, UnprocessableEntityResponseError, InternalServerErrorResponse), add PaymentRequiredResponseError for 402, add finance research and background research task error classes to auth/validation/500 tuples - models.py: add offset/include_domains/exclude_domains/boost_domains/ crawl_timeout to SearchInput, max_age to ContentsInput, background/ source_control/output_schema to ResearchInput, new AnswerInput and FinanceResearchInput dataclasses - activities.py: use you.search_async()/you.contents_async() direct methods, pass through all new params, add youdotcom_answer (you.answer_async), youdotcom_finance_research (you.finance_research_async), youdotcom_research_background (research_helpers.research_and_wait_async), fix research effort validation to include frontier - __init__.py: export new activities and input types - pyproject.toml: bump youdotcom from >=2.3.0,<3 to >=3.0.0,<4 - tests: 69 tests pass (ruff + mypy clean), covers all 6 activities, new params, error mapping for each endpoint - README: document all 6 activities with input field tables Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
- Add 8 integration tests covering all 6 activities against the real You.com API + local Temporal server (all pass in 69s) - Add timeout_s field to ResearchInput (used only by youdotcom_research_background): controls how long research_and_wait_async waits for SSE streaming before falling back to polling. Defaults to 120s in the activity; use 14400 for frontier tasks - Update unit tests: verify timeout_s passthrough + custom timeout test - Update README: document timeout_s parameter Integration test coverage: test_integration_search — real search, verify web results test_integration_search_with_domain_filters — include_domains filter test_integration_answer — real answer, verify citations test_integration_research — real research (lite), verify output test_integration_contents — real contents, verify markdown test_integration_finance_research — real finance research (deep) test_integration_research_background — background research with polling test_plugin_workflow_against_local_server — full Temporal workflow path Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
After [DX-695](https://linear.app/you-inc/issue/DX-695) closed the SDK 3.0.0 retarget gap, audit the diff against the Temporal contrib plugin patterns and the SDK's own conventions, then fill in the small plug-fixing gaps: - Ship a py.typed marker (and add it to the hatch wheel target) so downstream consumers get type checking on this plugin's public API. - Add a [dependency-groups] entry mirroring [project.optional-dependencies] to unlock 'pip install --group dev -e .' in CI alongside uv's --all-extras. - Refresh the pyproject description to mention Answer + Finance Research (previously only listed search/research/contents). - Add examples/hello_background_research_workflow.py and a launcher. The timeout_s vs start_to_close_timeout relationship is non-obvious and we hit the 600s SSE timeout during integration tests; the example makes it explicit for the frontier-effort case. - examples/run_workflow.py now generates a unique workflow id per invocation and sets an explicit id_reuse_policy so re-runs of the search example don't trip the default ALLOW_DUPLICATE_FAILED_ONLY behaviour. - Flesh out CHANGELOG.md [Unreleased] with Added/Changed/Fixed subsections covering the full SDK 3.0.0 retarget. Verified: ruff, mypy (7 files), 70 unit tests pass, wheel builds with 'youdotcom_temporal/py.typed' shipped, both example workflows import. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…rker serves both examples PR review caught that the README example commands reuse the same worker for run_workflow.py and run_background_research_workflow.py, but run_worker.py only registered HelloSearch. Running the background example against the worker would fail with WorkflowNotRegisteredError. Register both workflows; the launchers stay separate so each example still has its own command for copy-pasting. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…cy-groups] only Matches the SDK's own convention: [dependency-groups] is the single source of truth for dev tooling. [project.optional-dependencies] was a copy that would drift when one list was updated without the other. Updated README dev setup from 'uv sync --all-extras' to 'uv sync --group dev'. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…-3.0.0 Retarget Temporal Plugin to SDK 3.0.0
…opping [project.optional-dependencies] The previous commit removed [project.optional-dependencies] in favor of [dependency-groups], but left three references to 'uv sync --all-extras' in ci.yml, publish.yml, and CONTRIBUTING.md. With no extras defined, --all-extras is a no-op and dev tools (ruff, mypy, pytest) would not be installed in CI. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
… up README Staff-engineer review findings: 1. YouConfig.timeout_seconds default increased from 30s to 300s. The previous 30s default would cause httpx.ReadTimeout for deep/exhaustive inline research calls (60-300s). Search/answer/contents complete in <10s so the extra headroom is harmless; Temporal's start_to_close_timeout is the real wall-clock ceiling. 2. httpx.TimeoutException now mapped to ApplicationError(type= 'YouTimeoutError', non_retryable=False) instead of propagating as a raw httpx exception. Gives users a clean error type in the Temporal UI and lets Temporal retry with backoff. 3. README 'Plugin path' example no longer shows plugins=[YouPlugin()] on Client.connect — only the worker needs the plugin. run_worker.py already did this correctly; the README was inconsistent. 4. README error handling table now includes the YouTimeoutError row and a note about the 300s default. 5. CHANGELOG [Unreleased] updated with all post-review fixes: timeout increase, httpx timeout mapping, run_worker fix, dependency-group dedup, CI fix, README cleanup. Tests: 72 unit tests pass (added test_client_default_timeout_is_300s and test_http_timeout_maps_to_retryable_error). ruff + mypy clean. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…dabot, droid-review, filterwarnings Mirrors the You.com Python SDK repo's CI and packaging conventions: - CI: add concurrency cancellation (cancel-in-progress: true) to save CI minutes when a new push supersedes an in-progress run. - CI: add build-check job that builds the wheel and verifies py.typed is shipped — catches packaging regressions before merge. - Dependabot: weekly updates for uv dependencies and GitHub Actions, grouped by dev/runtime. Prevents dependency staleness. - Droid auto-review: automated PR review workflow (same as SDK repo). Skips external contributors; manual dispatch available for forks. - pyproject.toml: PEP 639 license declaration (license = 'MIT' + license-files = ['LICENSE']) replacing the old-style table form. - pytest: filterwarnings for ResourceWarning and PytestUnraisableExceptionWarning — catches leaked httpx transports during test runs, same enforcement as the SDK test suite. Verified: ruff, mypy, 72 unit tests pass (filterwarnings active, no resource leaks), wheel builds with new license format. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…-3.0.0 Post-merge fixes: timeout default, httpx error mapping, CI alignment
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 4 to 7. - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](astral-sh/setup-uv@v4...v7) --- updated-dependencies: - dependency-name: astral-sh/setup-uv dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- Version bumped from 0.1.0 to 1.0.0 - Classifier: Development Status :: 5 - Production/Stable - CHANGELOG [Unreleased] → [1.0.0] — 2026-08-07 Verified: 43/43 README + example checks pass against production You.com API and local Temporal server. Published to Test PyPI, install verified. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
tyler5673
marked this pull request as draft
August 11, 2026 00:19
- Add tests/test_replay_safety.py: run a workflow with a mocked SDK, record one call, replay the history, assert the SDK is not called again (the 'testing plugins for side effects' property from the Temporal AI partner review standards). - Add activity summary to the example workflows for Temporal UI debuggability.
…#10) * fix: resolve package exports lazily so Workflow files can import them youdotcom_temporal/__init__.py imported the Activity layer eagerly, which pulls in the You.com SDK and urllib.request. The workflow sandbox rejects urllib.request, so importing anything from this package inside a Workflow file failed at Worker construction with RestrictedWorkflowAccessError. Wrapping the import in workflow.unsafe.imports_passed_through() could not fix it: Python imports a parent package before the submodule body runs, so the passthrough block never got the chance to cover the parent's eager import. In practice a Worker only started if YouPlugin was also passed, because the plugin's sandbox passthrough list happened to cover the SDK -- an accidental coupling that nothing documented. Resolve the public names through a module __getattr__ (PEP 562) instead, so the SDK loads on first attribute access rather than at import time. Public imports are unchanged. Note the boundary: importing an Activity *function* still loads the SDK on any import scheme, since that is the module the function lives in. A Workflow that wants to stay SDK-free refers to its Activities by name, which is what the new test workflow does. Verified against SandboxedWorkflowRunner.prepare_workflow, the path Worker.__init__ takes for every registered Workflow. Tests cover the sandbox registration path, the no-SDK-on-import invariant (in a subprocess, since the test session imports the SDK elsewhere), and that every public name still resolves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test: run the Temporal-server tests in CI, and fix the one that never could CI never installed the temporal CLI, so test_replay_safety was skipped on all four Python versions on every run. An always-skipped test is indistinguishable from one that does not exist, and that gap is a large part of why the sandbox bug in this PR reached v1.0.0. Installing the CLI surfaced that test_replay_safety could never have passed anyway. Its Workflow was defined in the test module, and the sandbox re-imports the module a Workflow is defined in -- which re-executed the module-level shutil.which("temporal") in the file's own skipif marker, something the sandbox blocks. The test only ever "passed" by being skipped. Moving the Workflow into its own module fixes it; it now runs and passes. Also adds an end-to-end test for this PR's fix: a real dev server, a real Worker, a Workflow that imports the package at module scope, and no YouPlugin, so nothing supplies a sandbox passthrough. Confirmed it fails on main with "Failed validating workflow" and a restricted urllib.request, and passes here. prepare_workflow exercises the right code path but only a real Worker proves it. conftest.py puts the tests directory on sys.path so the helper Workflow modules import by name. Workflows have to live outside the test module for the reason above, and pytest's rootdir insertion did not reliably cover them. Suite goes from 76 passed / 1 skipped to 78 passed / 0 skipped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bump the package version for the 1.0.1 patch release, which ships the lazy package-export fix from #10. Also regenerates uv.lock, which had drifted: it still recorded version 0.1.0 and carried the [project.optional-dependencies] dev block that v1.0.0 removed when dev tooling moved to [dependency-groups].
Bumps [Factory-AI/droid-action](https://github.com/factory-ai/droid-action) from 5 to 6. - [Release notes](https://github.com/factory-ai/droid-action/releases) - [Commits](Factory-AI/droid-action@v5...v6) --- updated-dependencies: - dependency-name: Factory-AI/droid-action dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…ith 2 updates (#12) Bumps the dev-dependencies group with 2 updates in the / directory: [mypy](https://github.com/python/mypy) and [ruff](https://github.com/astral-sh/ruff). Updates `mypy` from 2.3.0 to 2.3.1 - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](python/mypy@v2.3.0...v2.3.1) Updates `ruff` from 0.16.1 to 0.16.3 - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](astral-sh/ruff@0.16.1...0.16.3) --- updated-dependencies: - dependency-name: mypy dependency-version: 2.3.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies - dependency-name: ruff dependency-version: 0.16.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…1.2 (#15) Replace the post-construction `user_agent` mutation with `app_name` / `app_version` constructor kwargs so the SDK's `X-Client-Info` header emits `client=youdotcom-temporal/<version>` on every outbound request. The SDK's own `user-agent` stays as `youdotcom-python-sdk/<v>`. - Bump `youdotcom>=3.0.0,<4` to `>=3.1.2,<4` - Drop `_USER_AGENT` constant and `sdk_configuration.user_agent` mutation - Update `tests/test_client.py` to assert the new constructor kwargs - Regenerate `uv.lock` Closes DX-784 Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Add `extraction: dict[str, Any] | None = None` to `SearchInput`, mirroring the SDK's `extraction` object (shipped in youdotcom 3.1.0). When set, it takes priority over `livecrawl` / `livecrawl_formats` and is passed to `you.search_async(extraction=...)` instead, avoiding the SDK's `ValueError` on dual-set. The legacy fields remain accepted for backward compatibility. - Add `extraction` field to `SearchInput` in `models.py` - Route `extraction` or `livecrawl`/`livecrawl_formats` in `activities.py` - Add tests for extraction path and priority-over-livecrawl - Update README SearchInput table with `extraction` row and deprecation notes - Update CHANGELOG Unreleased section Closes DX-721 Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR isn't meant to be merged — it's just here so Temporal's team has something to leave inline comments on for our AI Partner submission (https://temporal.io/partners/ai asks for a PR link so they can review against the Integrator's Guide).
Most of this repo was built via direct commits to
mainrather than through PRs, so there wasn't an existing one that covered the whole implementation. This one diffs everything built on top of the initial scaffold — config, error mapping, the SDK 3.0.0 retarget, CI, docs, and various fixes.One thing to flag: the very first commit (385d201, the original scaffold with the initial activities,
YouPlugin, and error mapping) can't show up in this diff. GitHub always computes a PR's diff from the merge-base of the two branches, and 385d201 is main's actual root commit — there's nothing before it to diff against. Getting it into this same PR would mean rewriting main's history, which would change the hash of every commit after it, including the one our published v1.0.0 release/PyPI package is tied to. Not worth it.So if you want eyes on that original commit too, it's viewable (and commentable) on its own here: 385d201 — since it's a root commit with no parent, GitHub shows its whole tree as one diff there.
Between that commit and this PR, that's the full implementation.
Related: the docs listing for this integration is at temporalio/documentation#5084.