Skip to content

Replace Go example server with Dojo implementation#2047

Merged
contextablemark merged 3 commits into
ag-ui-protocol:mainfrom
mattsp1290:chore/matt.spurlin/make-go-dojo-default-go-example
Jul 16, 2026
Merged

Replace Go example server with Dojo implementation#2047
contextablemark merged 3 commits into
ag-ui-protocol:mainfrom
mattsp1290:chore/matt.spurlin/make-go-dojo-default-go-example

Conversation

@mattsp1290

Copy link
Copy Markdown
Collaborator

Replaces the existing Go example server with an example I made that implements the full AG-UI spec.

@vercel

vercel Bot commented Jun 26, 2026

Copy link
Copy Markdown

@mattsp1290 is attempting to deploy a commit to the CopilotKit Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Python Preview Packages

Version 0.0.0.dev1784169442 published to TestPyPI.

Warning: These packages are built from contributor code that may not yet have been vetted for correctness or security. Install at your own risk and do not use in production.

Install with uv

Add the TestPyPI index to your pyproject.toml:

[[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
explicit = true

Then install the packages you need:

# Core SDK
uv add 'ag-ui-protocol==0.0.0.dev1784169442' --index testpypi

# Integrations (each already depends on the matching ag-ui-protocol preview)
uv add 'ag-ui-langgraph==0.0.0.dev1784169442' --index testpypi
uv add 'ag-ui-crewai==0.0.0.dev1784169442' --index testpypi
# NOTE: ag-ui-agent-spec depends on pyagentspec (git-only, not on PyPI).
# You will need to install pyagentspec separately from its git repo.
uv add 'ag-ui-agent-spec==0.0.0.dev1784169442' --index testpypi
uv add 'ag_ui_adk==0.0.0.dev1784169442' --index testpypi
uv add 'ag_ui_strands==0.0.0.dev1784169442' --index testpypi

Install with pip

pip install \
  --index-url https://test.pypi.org/simple/ \
  --extra-index-url https://pypi.org/simple/ \
  ag-ui-protocol==0.0.0.dev1784169442

Use --extra-index-url https://pypi.org/simple/ so pip can resolve
transitive dependencies (pydantic, fastapi, etc.) from real PyPI.


Commit: 849f1f5

@pkg-pr-new

pkg-pr-new Bot commented Jun 26, 2026

Copy link
Copy Markdown

Open in StackBlitz

@ag-ui/a2a-middleware

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/a2a-middleware@2047

@ag-ui/a2ui-middleware

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/a2ui-middleware@2047

@ag-ui/event-throttle-middleware

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/event-throttle-middleware@2047

@ag-ui/mcp-apps-middleware

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/mcp-apps-middleware@2047

@ag-ui/mcp-middleware

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/mcp-middleware@2047

@ag-ui/a2a

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/a2a@2047

@ag-ui/adk

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/adk@2047

@ag-ui/ag2

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/ag2@2047

@ag-ui/agno

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/agno@2047

@ag-ui/aws-strands

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/aws-strands@2047

@ag-ui/claude-agent-sdk

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/claude-agent-sdk@2047

@ag-ui/crewai

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/crewai@2047

@ag-ui/langchain

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/langchain@2047

@ag-ui/langgraph

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/langgraph@2047

@ag-ui/llamaindex

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/llamaindex@2047

@ag-ui/mastra

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/mastra@2047

@ag-ui/pydantic-ai

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/pydantic-ai@2047

@ag-ui/vercel-ai-sdk

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/vercel-ai-sdk@2047

@ag-ui/watsonx

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/watsonx@2047

@ag-ui/a2ui-toolkit

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/a2ui-toolkit@2047

create-ag-ui-app

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/create-ag-ui-app@2047

@ag-ui/client

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/client@2047

@ag-ui/core

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/core@2047

@ag-ui/encoder

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/encoder@2047

@ag-ui/proto

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/proto@2047

commit: cce5da2

@mattsp1290

Copy link
Copy Markdown
Collaborator Author

@NathanTarbert could I request a review on this?

@contextablemark contextablemark left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good overall : the event mapping is thorough and the interrupt/resume handling in loop.go is well done. One correctness issue in the predictive route that's worth fixing before merge.

In sdks/community/go/example/server/internal/agent/predictive.go, Run closes the llm step unconditionally after the stream helper returns:

emit.StepStarted("llm")
full, ok := p.streamPredictive(ctx, emit, doc, messages)
emit.StepFinished("llm")   // <-- runs even when streamPredictive already emitted RUN_ERROR
if !ok {
    return
}

But unlike streamTurn (which returns an error and lets the caller close the step before emitting RUN_ERROR), streamPredictive emits RUN_ERROR itself on a model/stream failure — the initial Stream(...) error and the mid-stream Recv error both call emit.RunError(...) and return ok=false. So when the model backend fails (rate limit, API error, mid-stream drop — anything that isn't a client disconnect), the wire sequence becomes:

RUN_STARTED, STATE_SNAPSHOT, STEP_STARTED(llm), RUN_ERROR, STEP_FINISHED(llm)

That STEP_FINISHED after RUN_ERROR is rejected by the SDK's own sequence validator. verifyEvents runs in the default TS/Dojo client pipeline (sdks/typescript/packages/client/src/agent/agent.ts:218), and once it sees RUN_ERROR it throws on any subsequent event (verify/verify.ts:43-50). So instead of surfacing a clean RUN_ERROR, the client's run pipeline throws AGUIError: Cannot send event type 'STEP_FINISHED': The run has already errored.

The other streaming routes are fine because they go through streamTurn's return-the-error contract (loop.go, sharedstate.go close the step before emit.RunError), and agentic_generative_ui's error path emits RUN_ERROR while a step is open but then returns without a STEP_FINISHED (which the validator allows — it only checks active-step state on RUN_FINISHED). Predictive is the sole outlier because its bespoke stream loop emits the terminal event inline.

Suggested fix — align streamPredictive with the streamTurn contract (return the error, let Run close the step first):

emit.StepStarted("llm")
full, err := p.streamPredictive(ctx, emit, doc, messages)
emit.StepFinished("llm")
if err != nil {
    if !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) && emit.Err() == nil {
        emit.RunError("the agent failed to generate a response")
    }
    return
}

with streamPredictive returning (string, error) and no longer calling emit.RunError itself. (A minimal alternative is to reorder so StepFinished fires before any RunError, but matching streamTurn keeps all four streaming paths on one error-emission discipline, which is what prevents this class of bug from recurring.)

Also worth adding: predictive_test.go currently doesn't exercise the model-error path, which is why this slipped through — a test that stubs a failing stream and asserts no event follows RUN_ERROR would lock it in.

Please let me know if my analysis got this wrong, but this seems like an edge case worth addressing before a merge.

@mattsp1290

Copy link
Copy Markdown
Collaborator Author

Looks good overall : the event mapping is thorough and the interrupt/resume handling in loop.go is well done. One correctness issue in the predictive route that's worth fixing before merge.

In sdks/community/go/example/server/internal/agent/predictive.go, Run closes the llm step unconditionally after the stream helper returns:

emit.StepStarted("llm")
full, ok := p.streamPredictive(ctx, emit, doc, messages)
emit.StepFinished("llm")   // <-- runs even when streamPredictive already emitted RUN_ERROR
if !ok {
    return
}

But unlike streamTurn (which returns an error and lets the caller close the step before emitting RUN_ERROR), streamPredictive emits RUN_ERROR itself on a model/stream failure — the initial Stream(...) error and the mid-stream Recv error both call emit.RunError(...) and return ok=false. So when the model backend fails (rate limit, API error, mid-stream drop — anything that isn't a client disconnect), the wire sequence becomes:

RUN_STARTED, STATE_SNAPSHOT, STEP_STARTED(llm), RUN_ERROR, STEP_FINISHED(llm)

That STEP_FINISHED after RUN_ERROR is rejected by the SDK's own sequence validator. verifyEvents runs in the default TS/Dojo client pipeline (sdks/typescript/packages/client/src/agent/agent.ts:218), and once it sees RUN_ERROR it throws on any subsequent event (verify/verify.ts:43-50). So instead of surfacing a clean RUN_ERROR, the client's run pipeline throws AGUIError: Cannot send event type 'STEP_FINISHED': The run has already errored.

The other streaming routes are fine because they go through streamTurn's return-the-error contract (loop.go, sharedstate.go close the step before emit.RunError), and agentic_generative_ui's error path emits RUN_ERROR while a step is open but then returns without a STEP_FINISHED (which the validator allows — it only checks active-step state on RUN_FINISHED). Predictive is the sole outlier because its bespoke stream loop emits the terminal event inline.

Suggested fix — align streamPredictive with the streamTurn contract (return the error, let Run close the step first):

emit.StepStarted("llm")
full, err := p.streamPredictive(ctx, emit, doc, messages)
emit.StepFinished("llm")
if err != nil {
    if !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) && emit.Err() == nil {
        emit.RunError("the agent failed to generate a response")
    }
    return
}

with streamPredictive returning (string, error) and no longer calling emit.RunError itself. (A minimal alternative is to reorder so StepFinished fires before any RunError, but matching streamTurn keeps all four streaming paths on one error-emission discipline, which is what prevents this class of bug from recurring.)

Also worth adding: predictive_test.go currently doesn't exercise the model-error path, which is why this slipped through — a test that stubs a failing stream and asserts no event follows RUN_ERROR would lock it in.

Please let me know if my analysis got this wrong, but this seems like an edge case worth addressing before a merge.

Nice catch! Refactored predictive streaming to close STEP_FINISHED before emitting RUN_ERROR and added regression tests for initial, mid-stream, cancellation, and deadline failures

@contextablemark contextablemark left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks great... even better than I suggested!

@contextablemark contextablemark merged commit 0e35504 into ag-ui-protocol:main Jul 16, 2026
9 checks passed
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.

2 participants