Skip to content

feat: add poly functions commands and grouped CLI help (DEVP-541, DEVP-543) - #245

Open
andymohajeri wants to merge 5 commits into
mainfrom
DEVP-541/integrate-functions-api-into-adk
Open

feat: add poly functions commands and grouped CLI help (DEVP-541, DEVP-543)#245
andymohajeri wants to merge 5 commits into
mainfrom
DEVP-541/integrate-functions-api-into-adk

Conversation

@andymohajeri

@andymohajeri andymohajeri commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a poly functions command group wrapping the public Functions REST API, so developers can manage Functions programmatically instead of only through the local-file/decorator workflow. Also groups the CLI's subcommand listings under section headers, since this PR pushes the top-level command count to 23.

Motivation

A new public Functions REST API was built across platform_ui (Sourcerer/agent-stream) and poly_core. This surfaces it in the ADK for headless/programmatic use.

Note: the ADK already has a "functions" concept (poly/resources/function.py) — local .py files with @func_description/@func_parameter decorators, synced via gRPC/protobuf through push/pull. That mechanism is unrelated and untouched here; the two are intentionally kept separate, with a docs cross-reference so the distinction is discoverable. Unifying them is a likely follow-up.

DEVP-543 is bundled in at reviewer request. The two changes are independent and are separate commits.

Changes

poly functions (DEVP-541)

Follows the audio-cache / conversations command pattern.

  • src/poly/cli_commands/functions.py (new): FunctionsCommand with list, get, create, update, delete, execute, duplicate, deploy, validate, references, type-definitions, deployments, and start/end for the branch lifecycle hooks. All support --json. Registered in cli.py via the usual 2-line import + COMMANDS entry.
  • src/poly/handlers/platform_api.py: FUNCTIONS_* URL constants and the corresponding PlatformAPIHandler methods. 409 conflicts (orphaned flow-step references, name collisions) are translated into FunctionConflictError, a ValueError subclass carrying orphaned_references so --force and the CLI's conflict reporting can use them.
  • src/poly/handlers/interface.py: AgentStudioInterface passthroughs.
  • src/poly/output/console.py: print_functions, print_function_detail, print_function_references, print_function_deployments, print_function_validation_issues, and a shared print_code.
  • Docs: a poly functions section in docs/docs/reference/cli.md, plus a cross-reference from the local-file Functions docs.

Unreadable --code-file paths and malformed --parameters / --args JSON exit with a clear message rather than a traceback.

Grouped --help sections (DEVP-543)

argparse has no built-in support for section headers within a subparsers group. Implements the ticket's option 2 — a custom HelpFormatter driven by a per-parser {subcommand: group} mapping — so each subcommand's own help= stays the single source of truth.

  • poly --help: Getting started / Project sync / Builder API / Other
  • poly functions --help: Manage / Run and inspect / Deploy / Lifecycle hooks
  • poly branch --help: Branch lifecycle / Inspect

Mechanism lives in cli_commands/base.py: add_grouped_subparsers() creates the subparsers action and drops the outer positional arguments: heading; group_subcommands() reorders the help entries and injects header rows; _GroupedHelpMixin hides the redundant metavar placeholder and tidies whitespace, mixed into both a plain and a raw-text formatter so parsers with hand-formatted descriptions still work. BaseCommand gains a group attribute.

Parsing, error messages and shell completion are unchanged.

Overlap with #247: that PR concurrently rewrites branch.py and takes it from 9 to 15 subcommands, so whoever merges second resolves a conflict in branch.py, interface.py and cli_test.py (the latter two are append-adjacent — take both sides) and extends BRANCH_SUBCOMMAND_GROUPS with the new subcommands. The coverage test fails until they do, so it can't be missed silently.

Reviewer note: this necessarily touches three semi-private argparse attributes (_choices_actions, _action_groups, and a _format_action override) — the ticket anticipates this for option 2. Each has a comment explaining the mechanism, and the tests assert on rendered output so a future Python change fails loudly rather than silently degrading.

Test strategy

  • Added/updated unit tests
  • Manual CLI testing (poly <command>) — --help for every new command and subcommand, plus both error paths (missing and invalid subcommand) on each grouped parser
  • Tested against a live Agent Studio project — not done: no live credentials/region access in this environment. Worth a smoke test against dev before merge.
  • N/A

Handler tests live in src/poly/tests/api/platform_api_test.py, CLI tests in src/poly/tests/cli_test.py. Coverage includes the 409-conflict paths (create/update/delete/duplicate), the 204-empty-body delete, and the input-validation exits. Each grouped parser has a test asserting its mapping covers every subcommand it registers, so adding one without grouping it fails CI.

Checklist

  • ruff check . and ruff format --check . pass
  • pytest passes (1053 passed)
  • No breaking changes to the poly CLI interface — poly functions is new, and the --help change is presentational
  • Commit messages follow conventional commits

Screenshots / Logs

poly --help:

usage: poly [-h] [-v] <command> ...

Getting started:
    init               Initialize a new Agent Studio project.
    start              Get started with PolyAI Agent Studio
    login              Log in to an existing Agent Studio account
    studio             Open the current project in Agent Studio (web).
    project            Manage Agent Studio projects.

Project sync:
    pull               Pull the latest project configuration from Agent
                       Studio.
    push               Push the project configuration to Agent Studio.
    status             Check the changed files of the project.
    revert             Revert changes in the project.
    format             Run ruff and YAML/JSON formatting on the project
                       (optional ty with --ty).
    validate           Validate the project configuration locally.
    diff               Show the changes made to the project.
    review             Create a GitHub Gist of Agent Studio project changes to
                       share changes.
    branch             Manage branches in the Agent Studio project.

Builder API:
    deployments        Manage deployments for the project.
    conversations      List and inspect conversations.
    audio-cache        Manage cached TTS audio via the Audio Cache API.
    functions          Manage Functions via the public Functions REST API.
    test               Manage and run tests for the project.
    rtc                Manage Real-Time Configuration.
    chat               Start an interactive chat session with the agent.

Other:
    docs               Outputs documentation for a given topic.
    completion         Generate shell completion scripts

options:
  -h, --help           show this help message and exit
  -v, --version        show the version and exit

poly functions --help:

Manage:
    list               List functions on the current branch.
    get                Get a single function by ID.
    create             Create a new function.
    update             Update an existing function.
    delete             Delete a function.
    duplicate          Duplicate a function.

Run and inspect:
    execute            Execute a function with the given arguments.
    references         Show flow steps that reference a function.
    type-definitions   Show Python type stubs for a function, for IDE autocomplete.

Deploy:
    deploy             Deploy all draft functions on the current branch.
    validate           Validate all functions on the current branch.
    deployments        List function deployment history across environments.

Lifecycle hooks:
    start              Manage the branch's start_function.
    end                Manage the branch's end_function.

🤖 Generated with Claude Code

@andymohajeri
andymohajeri requested a review from a team July 29, 2026 18:04
@andymohajeri
andymohajeri requested a review from a team as a code owner July 29, 2026 18:05
@linear-code

linear-code Bot commented Jul 29, 2026

Copy link
Copy Markdown

DEVP-541

DEVP-543

Comment thread src/poly/tests/functions_api_test.py Outdated
Surfaces the public Functions REST API in the ADK so developers can manage
Functions programmatically, following the existing audio-cache/conversations
command pattern:

- poly functions list/get/create/update/delete/execute/duplicate
- poly functions deploy/validate/references/type-definitions/deployments
- poly functions start|end get/update for the branch lifecycle hooks
- PlatformAPIHandler/AgentStudioInterface methods for the above, with 409
  conflicts translated into FunctionConflictError carrying the orphaned
  flow-step references
- print_functions/print_function_* and print_code helpers in console.py
- Handler tests (platform_api_test.py) and CLI tests (cli_test.py)
- cli.md reference docs, plus a cross-reference from the local-file Functions
  docs distinguishing the two mechanisms

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@andymohajeri
andymohajeri force-pushed the DEVP-541/integrate-functions-api-into-adk branch from b260b1c to 10d24c1 Compare August 4, 2026 17:37
@github-actions

This comment has been minimized.

@andymohajeri
andymohajeri force-pushed the DEVP-541/integrate-functions-api-into-adk branch from 10d24c1 to 9444f08 Compare August 4, 2026 17:43
@github-actions

This comment has been minimized.

@andymohajeri
andymohajeri force-pushed the DEVP-541/integrate-functions-api-into-adk branch from 9444f08 to 279a307 Compare August 4, 2026 17:48
@github-actions

This comment has been minimized.

@andymohajeri andymohajeri changed the title feat: integrate Functions REST API into ADK (DEVP-541) feat: add poly functions commands and grouped CLI help (DEVP-541, DEVP-543) Aug 4, 2026
@andymohajeri
andymohajeri force-pushed the DEVP-541/integrate-functions-api-into-adk branch from 279a307 to 3638899 Compare August 4, 2026 18:06
@github-actions

This comment has been minimized.

argparse has no built-in support for section headers within a subparsers
group, so the command lists were flat and getting hard to scan (23 at the top
level, 14 under `poly functions`, 9 under `poly branch`). Adds the general
mechanism the ticket describes as option 2 — a custom HelpFormatter driven by a
per-parser {subcommand: group} mapping — so each subcommand's own `help=` stays
the single source of truth.

Applied to every parser with a crowded subcommand list:

- `poly --help`: Getting started / Project sync / Builder API / Other
- `poly functions --help`: Manage / Run and inspect / Deploy / Lifecycle hooks
- `poly branch --help`: Branch lifecycle / Inspect

Mechanism, all in cli_commands/base.py:

- add_grouped_subparsers() creates the subparsers action, drops the outer
  "positional arguments:" heading and hoists the commands above the options
- group_subcommands(subparsers, {name: group}, order, fallback) reorders the
  help entries and injects header rows; a subcommand with an unmapped or
  misspelled group falls back to a trailing section rather than vanishing
- _GroupedHelpMixin hides the redundant metavar placeholder line and tidies the
  whitespace the header mechanism leaves behind, mixed into both a plain and a
  raw-text formatter so parsers with hand-formatted descriptions still work
- BaseCommand gains a `group` attribute for the top-level listing

Note: PR #247 concurrently rewrites branch.py and takes it from 9 to 15
subcommands, so whoever merges second resolves a conflict there and extends
BRANCH_SUBCOMMAND_GROUPS with the new subcommands. The coverage test fails
until they do, so it cannot be missed silently.

Parsing, error messages and shell completion are unchanged. Tests assert each
grouped parser's mapping covers every subcommand it registers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@andymohajeri
andymohajeri force-pushed the DEVP-541/integrate-functions-api-into-adk branch from 3638899 to 5ae1832 Compare August 4, 2026 18:10
@github-actions

This comment has been minimized.

@josephcesarperez josephcesarperez left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm functionally, just curious about the context for why we are introducing the usecase splitting out from the usual local pull push workflow

@Ruari-Phipps Ruari-Phipps left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah following on from what Joey said.
I think we need to allow for some commands to be handed account_id, project_id, region, branch_id directly rather than loading it from local file system.
Mainly the functions one here but we could also allow it for others

Comment thread docs/docs/reference/cli.md Outdated
delete_function always sent {"force": force} even when False, unlike
every other DELETE call in this handler. duplicate_function treated an
explicit empty-string name the same as omitting it, silently dropping
the value.
@github-actions

This comment has been minimized.

…mands

Address PR #245 review feedback (Ruari-Phipps):

- poly functions subcommands now accept --region/--project_id/--branch_id
  directly (all-or-nothing), bypassing the local project checkout entirely.
  Mirrors the existing `poly project` precedent for pre-checkout flags.
  Adds resolve_project_scope() in shared.py to pick explicit flags over
  load_project() when given.
- Move rtc/test/chat out of the "Builder API" --help section into
  "Project sync", since they operate on local project/branch state rather
  than being pure remote-API calls like functions/audio-cache/
  conversations/deployments.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

@andymohajeri

Copy link
Copy Markdown
Contributor Author

@josephcesarperez re: why split this off instead of the standard push/pull workflow — three main reasons:

  1. push/pull updates everything on the project, not just the resource being changed. It reads all of flows, entities, topics, agent settings, functions, etc. as one unit. Going through that flow to touch one function pulls the whole project state, which is slow, unnecessary for a single function update, and carries a bigger blast radius — if multiple CI jobs touch different functions, they can clobber each other's unrelated in-flight changes on a shared branch.

  2. Some function operations aren't config-sync at all. execute, deployments, validate, references are runtime/operational actions — run a function, check deployment history, check orphaned references. There's no push/pull equivalent for any of that.

  3. Different consumers want different things. push/pull is for devs working on a project who want git-style workflows (diff, review, branch). Some of the functions endpoints are meant to be called by CI, scripts, and other services — those want a scoped, stateless operation, not a full local checkout.

To be clear, this isn't replacing anything — the existing local .py-file/decorator Functions mechanism (poly/resources/function.py, synced via push/pull) is untouched and still the right tool for interactive local editing. This is additive, for the automation/headless case.

…nctions-api-into-adk

# Conflicts:
#	src/poly/cli_commands/branch.py
#	src/poly/handlers/interface.py
#	src/poly/tests/cli_test.py
@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report

Base (main) PR Change
75.1% 75.1% +0.0% ✅

Changed file coverage

File Coverage Change
poly/cli_commands/auth.py 22.7% +1.1% ✅
poly/cli_commands/testing.py 29.1% +0.7% ✅
poly/output/console.py 30.9% -1.5% ⚠️
poly/cli_commands/shared.py 49.3% +5.6% ✅
poly/cli_commands/project.py 52.8% +0.3% ✅
poly/cli_commands/rtc.py 57.6% +0.1% ✅
poly/cli_commands/branch.py 59.0% +0.3% ✅
poly/cli_commands/utils.py 62.3% +1.5% ✅
poly/cli_commands/sync.py 63.1% +0.8% ✅
poly/handlers/interface.py 63.5% +0.5% ✅
poly/cli_commands/chat.py 64.8% +0.1% ✅
poly/cli_commands/review.py 67.2% +0.2% ✅
poly/handlers/platform_api.py 75.5% +5.1% ✅
poly/cli.py 80.4% +1.6% ✅
poly/cli_commands/audio_cache.py 81.5% +0.1% ✅
poly/cli_commands/deployments.py 84.4% +0.0% ✅
poly/cli_commands/conversations.py 87.9% +0.2% ✅
poly/cli_commands/base.py 94.7% +8.1% ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants