Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3480874c28 |
+1
-2
@@ -25,8 +25,7 @@ below is kept current between tags and rolled into the next version when it ship
|
||||
- **A2A inbound AP2 mandate verification (opt-in)** — set `Options.AP2PublicKey` (or `a2a.WithPushURLPolicy`'s sibling `a2a.WithAP2PublicKey` for embedded handlers) and the gateway verifies AP2 payment/checkout mandates carried on incoming messages — signature and task/context binding — recording the outcome in each task's `ap2Verifications`, with the x402 settlement rail carried through for the paid path. Off by default; mandates are otherwise carried unverified. (`gateway/a2a/`)
|
||||
- **Flow human-in-the-loop pause/resume** — a flow step can suspend a run for external input with `flow.Await(key, prompt)` (or `flow.AwaitStep`): the run checkpoints with status `waiting` and `Execute` returns cleanly. `Flow.Waiting` lists suspended runs with what they await, and `Flow.ResumeWith(ctx, runID, input)` injects the input and continues from the next step. Recovery (`ResumePending`) skips waiting runs since they need input, not a restart. (`flow/`)
|
||||
- **Kubernetes reconcile core (alpha)** — `kubernetes.Reconcile(desired, observed)` decides the single action needed to converge an `Agent`/`Service`/`Flow` resource toward its Deployment (create / update / noop) and returns `Ready`/`Error` status conditions. Dependency-free (no controller-runtime / client-go) and fully unit-testable; a future operator binary supplies observed state and applies the action. (`deploy/kubernetes/`)
|
||||
- **In-process "local network" fast-path (opt-in)** — `client.Local()` lets a unary `Call` to a service running in the same process skip the network transport and dispatch straight to that server's handlers (for raw `codec/bytes.Frame` bodies — the shape agent/MCP/flow tool calls use), running the same router, wrappers, and codecs. In a benchmark this cut an in-process call from ~545µs to ~28µs (≈20×) with ~3.6× fewer allocations. Off by default; falls back to the network path for anything it doesn't cover. (`client/`, `server/`, `internal/network/`)
|
||||
- **`micro.Local()` service option** — turn on the in-process fast-path for a whole service in one place: every co-located unary call its client makes (agent tool calls, flow dispatch, gateway → service) takes the fast-path, with no per-call wiring. Off by default; a no-op for distributed deployments. (`service/`, root `options.go`)
|
||||
- **In-process dispatch fast-path (opt-in)** — `client.LocalDispatch()` lets a unary `Call` to a service running in the same process skip the network transport and dispatch straight to that server's handlers (for raw `codec/bytes.Frame` bodies — the shape agent/MCP/flow tool calls use), running the same router, wrappers, and codecs. In a benchmark this cut an in-process call from ~545µs to ~28µs (≈20×) with ~3.6× fewer allocations. Off by default; falls back to the network path for anything it doesn't cover. (`client/`, `server/`, `internal/network/`)
|
||||
|
||||
### Changed
|
||||
- **Remote agent chat streaming** — `micro chat` now streams replies from remote agents instead of waiting for the full response. (`cmd/micro/`, `agent/`)
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
Go Micro is an **agent harness** and service framework for Go.
|
||||
|
||||
## Overview
|
||||
**Community:** questions, ideas, or just want to build alongside us? [Join the Discord](https://discord.gg/G8Gk5j3uXr).
|
||||
|
||||
A harness is the runtime around an agent: the tools it can call, the memory it keeps, the guardrails that bound it, the workflows that trigger it, the services it depends on, and the protocols other agents use to reach it.
|
||||
|
||||
Go Micro gives you the harness as Go code. Build an agent and it gets a model, memory, tools, planning, delegation, guardrails, and service discovery; it is reachable over [MCP](https://modelcontextprotocol.io/) and [A2A](https://a2a-protocol.org). Write services and every endpoint becomes an AI-callable tool. Orchestrate the deterministic parts with durable flows. Agents, services, and flows share one runtime because an agent is a distributed system, and building one is building a service.
|
||||
@@ -17,10 +18,6 @@ Go Micro gives you the harness as Go code. Build an agent and it gets a model, m
|
||||
|
||||
**Want to support Go Micro and see your logo here?** [Become a sponsor](https://discord.gg/G8Gk5j3uXr) — reach out on Discord.
|
||||
|
||||
## Community
|
||||
|
||||
Questions, ideas, or just want to build alongside us? [Join the Discord](https://discord.gg/G8Gk5j3uXr).
|
||||
|
||||
## Commercial Support
|
||||
|
||||
Running Go Micro in production, or building on it and want help? Paid **support, consulting, training, and retainers** are available directly from the maintainer — and they're what keep the project maintained. See [**Support**](SUPPORT.md) for the tiers, or [open a request](https://github.com/micro/go-micro/issues/new?template=commercial_support.md).
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ import (
|
||||
"go-micro.dev/v6/transport/headers"
|
||||
)
|
||||
|
||||
// localCall is the in-process fast-path for Call. When Local is enabled
|
||||
// localCall is the in-process fast-path for Call. When LocalDispatch is enabled
|
||||
// and the callee runs in this same process, a unary request whose body and
|
||||
// response are raw frames (codec/bytes.Frame) is dispatched straight to the
|
||||
// server's handlers via internal/network — no dial, no codec-over-socket,
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
// service not registered in-process), so behavior is unchanged unless the
|
||||
// fast-path fully applies.
|
||||
func (r *rpcClient) localCall(ctx context.Context, req Request, resp interface{}) (handled bool, err error) {
|
||||
if !r.opts.Local || req.Stream() {
|
||||
if !r.opts.LocalDispatch || req.Stream() {
|
||||
return false, nil
|
||||
}
|
||||
reqFrame, ok := req.Body().(*raw.Frame)
|
||||
|
||||
+10
-10
@@ -81,15 +81,15 @@ func callEcho(t testing.TB, cl client.Client, msg string) EchoRsp {
|
||||
return out
|
||||
}
|
||||
|
||||
// TestLocalMatchesNetwork proves the in-process fast-path returns the
|
||||
// TestLocalDispatchMatchesNetwork proves the in-process fast-path returns the
|
||||
// exact same result as the network path for the same handler and request.
|
||||
func TestLocalMatchesNetwork(t *testing.T) {
|
||||
func TestLocalDispatchMatchesNetwork(t *testing.T) {
|
||||
reg := registry.NewMemoryRegistry()
|
||||
stop := startEchoServer(t, reg)
|
||||
defer stop()
|
||||
|
||||
net := newEchoClient(reg) // network path
|
||||
local := newEchoClient(reg, client.Local()) // in-process fast-path
|
||||
net := newEchoClient(reg) // network path
|
||||
local := newEchoClient(reg, client.LocalDispatch()) // in-process fast-path
|
||||
|
||||
netRsp := callEcho(t, net, "hi")
|
||||
localRsp := callEcho(t, local, "hi")
|
||||
@@ -102,16 +102,16 @@ func TestLocalMatchesNetwork(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestLocalFallsBackWhenNotLocal confirms a service not registered
|
||||
// in-process still works via the network path even with Local on.
|
||||
func TestLocalFallsBackWhenNotLocal(t *testing.T) {
|
||||
// TestLocalDispatchFallsBackWhenNotLocal confirms a service not registered
|
||||
// in-process still works via the network path even with LocalDispatch on.
|
||||
func TestLocalDispatchFallsBackWhenNotLocal(t *testing.T) {
|
||||
reg := registry.NewMemoryRegistry()
|
||||
stop := startEchoServer(t, reg)
|
||||
defer stop()
|
||||
|
||||
// Local is on, but the call still resolves — the fast-path only
|
||||
// LocalDispatch is on, but the call still resolves — the fast-path only
|
||||
// engages when it fully applies, otherwise the network path runs.
|
||||
local := newEchoClient(reg, client.Local())
|
||||
local := newEchoClient(reg, client.LocalDispatch())
|
||||
if got := callEcho(t, local, "x").Msg; got != "echo:x" {
|
||||
t.Fatalf("reply = %q, want echo:x", got)
|
||||
}
|
||||
@@ -136,4 +136,4 @@ func benchmarkEcho(b *testing.B, opts ...client.Option) {
|
||||
}
|
||||
|
||||
func BenchmarkNetworkCall(b *testing.B) { benchmarkEcho(b) }
|
||||
func BenchmarkLocalCall(b *testing.B) { benchmarkEcho(b, client.Local()) }
|
||||
func BenchmarkLocalCall(b *testing.B) { benchmarkEcho(b, client.LocalDispatch()) }
|
||||
|
||||
+5
-5
@@ -69,10 +69,10 @@ type Options struct {
|
||||
PoolTTL time.Duration
|
||||
PoolCloseTimeout time.Duration
|
||||
|
||||
// Local, when true, lets a unary Call to a service running in this
|
||||
// LocalDispatch, when true, lets a unary Call to a service running in this
|
||||
// same process skip the network transport and dispatch directly to that
|
||||
// server's handlers (raw byte bodies only). Off by default.
|
||||
Local bool
|
||||
LocalDispatch bool
|
||||
}
|
||||
|
||||
// CallOptions are options used to make calls to a server.
|
||||
@@ -186,14 +186,14 @@ func ContentType(ct string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Local enables the in-process fast-path: a unary Call to a service
|
||||
// LocalDispatch enables the in-process fast-path: a unary Call to a service
|
||||
// running in the same process dispatches straight to that server's handlers
|
||||
// (skipping dial, codec-over-socket, and the transport pump) when both request
|
||||
// and response bodies are raw frames (codec/bytes.Frame) — the shape agent,
|
||||
// MCP, and flow tool calls use. Falls back to the network path otherwise.
|
||||
func Local() Option {
|
||||
func LocalDispatch() Option {
|
||||
return func(o *Options) {
|
||||
o.Local = true
|
||||
o.LocalDispatch = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ var Broker = service.Broker
|
||||
var Cache = service.Cache
|
||||
var Cmd = service.Cmd
|
||||
var Client = service.Client
|
||||
var Local = service.Local
|
||||
var Context = service.Context
|
||||
var Handle = service.Handle
|
||||
var HandleSignal = service.HandleSignal
|
||||
|
||||
@@ -111,18 +111,6 @@ func Client(c client.Client) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Local enables the in-process fast-path on the service's client: a
|
||||
// unary call to another service running in the same process — agent tool calls,
|
||||
// flow dispatch, gateway → service — skips the network transport and dispatches
|
||||
// straight to that server's handlers (see client.Local). Off by
|
||||
// default; it falls back to the network path for anything not co-located, so
|
||||
// it is a pure win for all-in-one binaries and a no-op for distributed ones.
|
||||
func Local() Option {
|
||||
return func(o *Options) {
|
||||
_ = o.Client.Init(client.Local())
|
||||
}
|
||||
}
|
||||
|
||||
// Context specifies a context for the service.
|
||||
// Can be used to signal shutdown of the service and for extra option values.
|
||||
func Context(ctx context.Context) Option {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package service
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestLocalOption(t *testing.T) {
|
||||
// Off by default.
|
||||
if newOptions().Client.Options().Local {
|
||||
t.Fatal("Local should be off by default")
|
||||
}
|
||||
// Enabled by the option, on the service's own client.
|
||||
if !newOptions(Local()).Client.Options().Local {
|
||||
t.Fatal("Local() did not enable the client fast-path")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user