 5978a0b8f5
			
		
	
	5978a0b8f5
	
	
	
		
			
			- Agent roles and coordination features - Chat API integration testing - New configuration and workspace management 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			373 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			373 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) 2022 Uber Technologies, Inc.
 | |
| //
 | |
| // Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
| // of this software and associated documentation files (the "Software"), to deal
 | |
| // in the Software without restriction, including without limitation the rights
 | |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
| // copies of the Software, and to permit persons to whom the Software is
 | |
| // furnished to do so, subject to the following conditions:
 | |
| //
 | |
| // The above copyright notice and this permission notice shall be included in
 | |
| // all copies or substantial portions of the Software.
 | |
| //
 | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | |
| // THE SOFTWARE.
 | |
| 
 | |
| package fx
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 
 | |
| 	"go.uber.org/dig"
 | |
| 	"go.uber.org/fx/fxevent"
 | |
| 	"go.uber.org/fx/internal/fxreflect"
 | |
| 	"go.uber.org/multierr"
 | |
| )
 | |
| 
 | |
| // A container represents a set of constructors to provide
 | |
| // dependencies, and a set of functions to invoke once all the
 | |
| // dependencies have been initialized.
 | |
| //
 | |
| // This definition corresponds to the dig.Container and dig.Scope.
 | |
| type container interface {
 | |
| 	Invoke(interface{}, ...dig.InvokeOption) error
 | |
| 	Provide(interface{}, ...dig.ProvideOption) error
 | |
| 	Decorate(interface{}, ...dig.DecorateOption) error
 | |
| }
 | |
| 
 | |
| // Module is a named group of zero or more fx.Options.
 | |
| // A Module creates a scope in which certain operations are taken
 | |
| // place. For more information, see [Decorate], [Replace], or [Invoke].
 | |
| func Module(name string, opts ...Option) Option {
 | |
| 	mo := moduleOption{
 | |
| 		name:     name,
 | |
| 		location: fxreflect.CallerStack(1, 2)[0],
 | |
| 		options:  opts,
 | |
| 	}
 | |
| 	return mo
 | |
| }
 | |
| 
 | |
| type moduleOption struct {
 | |
| 	name     string
 | |
| 	location fxreflect.Frame
 | |
| 	options  []Option
 | |
| }
 | |
| 
 | |
| func (o moduleOption) String() string {
 | |
| 	return fmt.Sprintf("fx.Module(%q, %v)", o.name, o.options)
 | |
| }
 | |
| 
 | |
| func (o moduleOption) apply(mod *module) {
 | |
| 	// This get called on any submodules' that are declared
 | |
| 	// as part of another module.
 | |
| 
 | |
| 	// 1. Create a new module with the parent being the specified
 | |
| 	// module.
 | |
| 	// 2. Apply child Options on the new module.
 | |
| 	// 3. Append it to the parent module.
 | |
| 
 | |
| 	// Create trace as parent's trace with this module's location pre-pended.
 | |
| 	trace := append([]string{fmt.Sprintf("%v (%v)", o.location, o.name)}, mod.trace...)
 | |
| 	newModule := &module{
 | |
| 		name:   o.name,
 | |
| 		parent: mod,
 | |
| 		trace:  trace,
 | |
| 		app:    mod.app,
 | |
| 	}
 | |
| 	for _, opt := range o.options {
 | |
| 		opt.apply(newModule)
 | |
| 	}
 | |
| 	mod.modules = append(mod.modules, newModule)
 | |
| }
 | |
| 
 | |
| type module struct {
 | |
| 	parent         *module
 | |
| 	name           string
 | |
| 	trace          []string
 | |
| 	scope          scope
 | |
| 	provides       []provide
 | |
| 	invokes        []invoke
 | |
| 	decorators     []decorator
 | |
| 	modules        []*module
 | |
| 	app            *App
 | |
| 	log            fxevent.Logger
 | |
| 	fallbackLogger fxevent.Logger
 | |
| 	logConstructor *provide
 | |
| }
 | |
| 
 | |
| // scope is a private wrapper interface for dig.Container and dig.Scope.
 | |
| // We can consider moving this into Fx using type constraints after Go 1.20
 | |
| // is released and 1.17 is deprecated.
 | |
| type scope interface {
 | |
| 	Decorate(f interface{}, opts ...dig.DecorateOption) error
 | |
| 	Invoke(f interface{}, opts ...dig.InvokeOption) error
 | |
| 	Provide(f interface{}, opts ...dig.ProvideOption) error
 | |
| 	Scope(name string, opts ...dig.ScopeOption) *dig.Scope
 | |
| 	String() string
 | |
| }
 | |
| 
 | |
| // builds the Scopes using the App's Container. Note that this happens
 | |
| // after applyModules' are called because the App's Container needs to
 | |
| // be built for any Scopes to be initialized, and applys' should be called
 | |
| // before the Container can get initialized.
 | |
| func (m *module) build(app *App, root *dig.Container) {
 | |
| 	if m.parent == nil {
 | |
| 		m.scope = root
 | |
| 	} else {
 | |
| 		parentScope := m.parent.scope
 | |
| 		m.scope = parentScope.Scope(m.name)
 | |
| 		// use parent module's logger by default
 | |
| 		m.log = m.parent.log
 | |
| 	}
 | |
| 
 | |
| 	if m.logConstructor != nil {
 | |
| 		// Since user supplied a custom logger, use a buffered logger
 | |
| 		// to hold all messages until user supplied logger is
 | |
| 		// instantiated. Then we flush those messages after fully
 | |
| 		// constructing the custom logger.
 | |
| 		m.fallbackLogger, m.log = m.log, new(logBuffer)
 | |
| 	}
 | |
| 
 | |
| 	for _, mod := range m.modules {
 | |
| 		mod.build(app, root)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (m *module) provideAll() {
 | |
| 	for _, p := range m.provides {
 | |
| 		m.provide(p)
 | |
| 	}
 | |
| 
 | |
| 	for _, m := range m.modules {
 | |
| 		m.provideAll()
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (m *module) provide(p provide) {
 | |
| 	if m.app.err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if p.IsSupply {
 | |
| 		m.supply(p)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	funcName := fxreflect.FuncName(p.Target)
 | |
| 	var info dig.ProvideInfo
 | |
| 	opts := []dig.ProvideOption{
 | |
| 		dig.FillProvideInfo(&info),
 | |
| 		dig.Export(!p.Private),
 | |
| 		dig.WithProviderCallback(func(ci dig.CallbackInfo) {
 | |
| 			m.log.LogEvent(&fxevent.Run{
 | |
| 				Name:       funcName,
 | |
| 				Kind:       "provide",
 | |
| 				ModuleName: m.name,
 | |
| 				Err:        ci.Error,
 | |
| 			})
 | |
| 		}),
 | |
| 	}
 | |
| 
 | |
| 	if err := runProvide(m.scope, p, opts...); err != nil {
 | |
| 		m.app.err = err
 | |
| 	}
 | |
| 	outputNames := make([]string, len(info.Outputs))
 | |
| 	for i, o := range info.Outputs {
 | |
| 		outputNames[i] = o.String()
 | |
| 	}
 | |
| 
 | |
| 	m.log.LogEvent(&fxevent.Provided{
 | |
| 		ConstructorName: funcName,
 | |
| 		StackTrace:      p.Stack.Strings(),
 | |
| 		ModuleTrace:     append([]string{p.Stack[0].String()}, m.trace...),
 | |
| 		ModuleName:      m.name,
 | |
| 		OutputTypeNames: outputNames,
 | |
| 		Err:             m.app.err,
 | |
| 		Private:         p.Private,
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func (m *module) supply(p provide) {
 | |
| 	typeName := p.SupplyType.String()
 | |
| 	opts := []dig.ProvideOption{
 | |
| 		dig.Export(!p.Private),
 | |
| 		dig.WithProviderCallback(func(ci dig.CallbackInfo) {
 | |
| 			m.log.LogEvent(&fxevent.Run{
 | |
| 				Name:       fmt.Sprintf("stub(%v)", typeName),
 | |
| 				Kind:       "supply",
 | |
| 				ModuleName: m.name,
 | |
| 			})
 | |
| 		}),
 | |
| 	}
 | |
| 
 | |
| 	if err := runProvide(m.scope, p, opts...); err != nil {
 | |
| 		m.app.err = err
 | |
| 	}
 | |
| 
 | |
| 	m.log.LogEvent(&fxevent.Supplied{
 | |
| 		TypeName:    typeName,
 | |
| 		StackTrace:  p.Stack.Strings(),
 | |
| 		ModuleTrace: append([]string{p.Stack[0].String()}, m.trace...),
 | |
| 		ModuleName:  m.name,
 | |
| 		Err:         m.app.err,
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // Constructs custom loggers for all modules in the tree
 | |
| func (m *module) constructAllCustomLoggers() {
 | |
| 	if m.logConstructor != nil {
 | |
| 		if buffer, ok := m.log.(*logBuffer); ok {
 | |
| 			// default to parent's logger if custom logger constructor fails
 | |
| 			if err := m.constructCustomLogger(buffer); err != nil {
 | |
| 				m.app.err = multierr.Append(m.app.err, err)
 | |
| 				m.log = m.fallbackLogger
 | |
| 				buffer.Connect(m.log)
 | |
| 			}
 | |
| 		}
 | |
| 		m.fallbackLogger = nil
 | |
| 	} else if m.parent != nil {
 | |
| 		m.log = m.parent.log
 | |
| 	}
 | |
| 
 | |
| 	for _, mod := range m.modules {
 | |
| 		mod.constructAllCustomLoggers()
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Mirroring the behavior of app.constructCustomLogger
 | |
| func (m *module) constructCustomLogger(buffer *logBuffer) (err error) {
 | |
| 	p := m.logConstructor
 | |
| 	fname := fxreflect.FuncName(p.Target)
 | |
| 	defer func() {
 | |
| 		m.log.LogEvent(&fxevent.LoggerInitialized{
 | |
| 			Err:             err,
 | |
| 			ConstructorName: fname,
 | |
| 		})
 | |
| 	}()
 | |
| 
 | |
| 	// TODO: Use dig.FillProvideInfo to inspect the provided constructor
 | |
| 	// and fail the application if its signature didn't match.
 | |
| 	if err := m.scope.Provide(p.Target); err != nil {
 | |
| 		return fmt.Errorf("fx.WithLogger(%v) from:\n%+v\nin Module: %q\nFailed: %w",
 | |
| 			fname, p.Stack, m.name, err)
 | |
| 	}
 | |
| 
 | |
| 	return m.scope.Invoke(func(log fxevent.Logger) {
 | |
| 		m.log = log
 | |
| 		buffer.Connect(log)
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func (m *module) executeInvokes() error {
 | |
| 	for _, m := range m.modules {
 | |
| 		if err := m.executeInvokes(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for _, invoke := range m.invokes {
 | |
| 		if err := m.executeInvoke(invoke); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *module) executeInvoke(i invoke) (err error) {
 | |
| 	fnName := fxreflect.FuncName(i.Target)
 | |
| 	m.log.LogEvent(&fxevent.Invoking{
 | |
| 		FunctionName: fnName,
 | |
| 		ModuleName:   m.name,
 | |
| 	})
 | |
| 	err = runInvoke(m.scope, i)
 | |
| 	m.log.LogEvent(&fxevent.Invoked{
 | |
| 		FunctionName: fnName,
 | |
| 		ModuleName:   m.name,
 | |
| 		Err:          err,
 | |
| 		Trace:        fmt.Sprintf("%+v", i.Stack), // format stack trace as multi-line
 | |
| 	})
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func (m *module) decorateAll() error {
 | |
| 	for _, d := range m.decorators {
 | |
| 		if err := m.decorate(d); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for _, m := range m.modules {
 | |
| 		if err := m.decorateAll(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *module) decorate(d decorator) (err error) {
 | |
| 	if d.IsReplace {
 | |
| 		return m.replace(d)
 | |
| 	}
 | |
| 
 | |
| 	funcName := fxreflect.FuncName(d.Target)
 | |
| 	var info dig.DecorateInfo
 | |
| 	opts := []dig.DecorateOption{
 | |
| 		dig.FillDecorateInfo(&info),
 | |
| 		dig.WithDecoratorCallback(func(ci dig.CallbackInfo) {
 | |
| 			m.log.LogEvent(&fxevent.Run{
 | |
| 				Name:       funcName,
 | |
| 				Kind:       "decorate",
 | |
| 				ModuleName: m.name,
 | |
| 				Err:        ci.Error,
 | |
| 			})
 | |
| 		}),
 | |
| 	}
 | |
| 
 | |
| 	err = runDecorator(m.scope, d, opts...)
 | |
| 	outputNames := make([]string, len(info.Outputs))
 | |
| 	for i, o := range info.Outputs {
 | |
| 		outputNames[i] = o.String()
 | |
| 	}
 | |
| 
 | |
| 	m.log.LogEvent(&fxevent.Decorated{
 | |
| 		DecoratorName:   funcName,
 | |
| 		StackTrace:      d.Stack.Strings(),
 | |
| 		ModuleTrace:     append([]string{d.Stack[0].String()}, m.trace...),
 | |
| 		ModuleName:      m.name,
 | |
| 		OutputTypeNames: outputNames,
 | |
| 		Err:             err,
 | |
| 	})
 | |
| 
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func (m *module) replace(d decorator) error {
 | |
| 	typeName := d.ReplaceType.String()
 | |
| 	opts := []dig.DecorateOption{
 | |
| 		dig.WithDecoratorCallback(func(ci dig.CallbackInfo) {
 | |
| 			m.log.LogEvent(&fxevent.Run{
 | |
| 				Name:       fmt.Sprintf("stub(%v)", typeName),
 | |
| 				Kind:       "replace",
 | |
| 				ModuleName: m.name,
 | |
| 				Err:        ci.Error,
 | |
| 			})
 | |
| 		}),
 | |
| 	}
 | |
| 
 | |
| 	err := runDecorator(m.scope, d, opts...)
 | |
| 	m.log.LogEvent(&fxevent.Replaced{
 | |
| 		ModuleName:      m.name,
 | |
| 		StackTrace:      d.Stack.Strings(),
 | |
| 		ModuleTrace:     append([]string{d.Stack[0].String()}, m.trace...),
 | |
| 		OutputTypeNames: []string{typeName},
 | |
| 		Err:             err,
 | |
| 	})
 | |
| 	return err
 | |
| }
 |