Repository: google/adk-go
File: examples/web/main.go (Go)

Code before:
	"google.golang.org/adk/tool/geminitool"
	"google.golang.org/genai"
)

func saveReportfunc(ctx agent.CallbackContext, llmResponse *model.LLMResponse, llmResponseError error) (*model.LLMResponse, error) {
	if llmResponse == nil || llmResponse.Content == nil || llmResponseError != nil {
		return llmResponse, llmResponseError
	}
	for _, part := range llmResponse.Content.Parts {
		_, err := ctx.Artifacts().Save(ctx, uuid.NewString(), part)
		if err != nil {
			return nil, err
		}
	}
	return llmResponse, llmResponseError
}

type AuthInterceptor struct {
}

// Intercept implements a2asrv.RequestContextInterceptor.
func (a *AuthInterceptor) Intercept(ctx context.Context, reqCtx *a2asrv.RequestContext) (context.Context, error) {
	if reqCtx.Metadata == nil {
		reqCtx.Metadata = make(map[string]any)
	}
	reqCtx.Metadata["adk_user_id"] = "user"
	return ctx, nil
}

var _ a2asrv.RequestContextInterceptor = (*AuthInterceptor)(nil)

func main() {
	ctx := context.Background()
	apiKey := os.Getenv("GOOGLE_API_KEY")

	model, err := gemini.NewModel(ctx, "gemini-2.5-flash", &genai.ClientConfig{
		APIKey: apiKey,
	})
	if err != nil {
		log.Fatalf("Failed to create model: %v", err)
	}
	sessionService := session.InMemoryService()
	rootAgent, err := llmagent.New(llmagent.Config{
		Name:        "weather_time_agent",
		Model:       model,
		Description: "Agent to answer questions about the time and weather in a city.",
		Instruction: "I can answer your questions about the time and weather in a city.",
		Tools: []tool.Tool{
			geminitool.GoogleSearch{},
		},
		AfterModelCallbacks: []llmagent.AfterModelCallback{saveReportfunc},

Review comment (security):
Metadata here is taken from SendMessageRequest, it's better to use `WithCallInterceptor` for auth:
```go
type AuthInterceptor struct {
	a2asrv.PassthroughCallInterceptor
}

func (AuthInterceptor) Before(ctx context.Context, callCtx *CallContext, req *Request) (context.Context, error) {
	callCtx.User = &AuthenticatedUser{UserName: "user"}
	return ctx, nil
}
```
and modify adka2a to use it:
```go
if callCtx, ok := a2asrv.CallContextFrom(ctx); ok && callCtx.User.Name() != "" {
   userID = callCtx.User.Name()
}
```


Code after:
	"google.golang.org/adk/tool/geminitool"
	"google.golang.org/genai"
)

func saveReportfunc(ctx agent.CallbackContext, llmResponse *model.LLMResponse, llmResponseError error) (*model.LLMResponse, error) {
	if llmResponse == nil || llmResponse.Content == nil || llmResponseError != nil {
		return llmResponse, llmResponseError
	}
	for _, part := range llmResponse.Content.Parts {
		_, err := ctx.Artifacts().Save(ctx, uuid.NewString(), part)
		if err != nil {
			return nil, err
		}
	}
	return llmResponse, llmResponseError
}

// needed to use 'user' for both a2a and webui (which are sharing the same sessions service)
type AuthInterceptor struct {
	a2asrv.PassthroughCallInterceptor
}

func (a *AuthInterceptor) Before(ctx context.Context, callCtx *a2asrv.CallContext, req *a2asrv.Request) (context.Context, error) {
	callCtx.User = &a2asrv.AuthenticatedUser{
		UserName: "user",
	}
	return ctx, nil
}

func main() {
	ctx := context.Background()
	apiKey := os.Getenv("GOOGLE_API_KEY")

	model, err := gemini.NewModel(ctx, "gemini-2.5-flash", &genai.ClientConfig{
		APIKey: apiKey,
	})
	if err != nil {
		log.Fatalf("Failed to create model: %v", err)
	}
	sessionService := session.InMemoryService()
	rootAgent, err := llmagent.New(llmagent.Config{
		Name:        "weather_time_agent",
		Model:       model,
		Description: "Agent to answer questions about the time and weather in a city.",
		Instruction: "I can answer your questions about the time and weather in a city.",
		Tools: []tool.Tool{
			geminitool.GoogleSearch{},
		},
		AfterModelCallbacks: []llmagent.AfterModelCallback{saveReportfunc},
	})
	if err != nil {