Conversation
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
📝 WalkthroughWalkthroughThis pull request introduces a comprehensive AI management system for the framework, including contract interfaces for AI orchestration (AI, Conversation, Agent, Provider, Response, Usage, Message, Option), implementations with Application and ServiceProvider, helper option functions, integration with the container and facade system, and extensive auto-generated mock implementations for testing. Changes
Sequence DiagramsequenceDiagram
participant App as Application
participant AI as AI Manager
participant Conv as Conversation
participant Prov as Provider
participant Resp as Response
App->>AI: WithContext(ctx)
activate AI
AI-->>App: AI instance (new context)
deactivate AI
App->>AI: Agent(agent, options...)
activate AI
AI->>Conv: Create conversation with options
activate Conv
Conv-->>AI: Conversation instance
deactivate Conv
AI-->>App: Conversation
deactivate AI
App->>Conv: Prompt(input)
activate Conv
Conv->>Prov: Prompt(ctx, agent, messages, options)
activate Prov
Prov-->>Resp: Response
deactivate Prov
Conv->>Conv: Update messages history
Conv-->>App: Response
deactivate Conv
App->>Conv: Messages()
activate Conv
Conv-->>App: []Message
deactivate Conv
App->>Conv: Reset()
activate Conv
Conv->>Conv: Clear messages, restore initial
deactivate Conv
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 1 | ❌ 4❌ Failed checks (4 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Tip CodeRabbit can approve the review once all CodeRabbit's comments are resolved.Enable the |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (3)
mocks/ai/Ai.go (1)
28-33: Update mock generation template to useanyinstead ofinterface{}.The mocks still emit
interface{}in multiple places. Please adjust the mock generation config/template so that regenerated files follow the project Go style rule of usinganyinstead ofinterface{}(applies tomocks/ai/Ai.go,mocks/ai/AI.go,mocks/ai/Provider.go,mocks/ai/Response.go,mocks/ai/Usage.go, and other generated mock files).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mocks/ai/Ai.go` around lines 28 - 33, The generated mocks use interface{} instead of Go's preferred alias any; update the mock generation template/config so all occurrences emit any (e.g., change make([]interface{}, len(options)) -> make([]any, len(options)) and var _ca []interface{} -> var _ca []any) and then regenerate the mocks (check files like mocks/ai/Ai.go, Ai.go symbols _va, options, _ca, agent and other generated files such as AI.go, Provider.go, Response.go, Usage.go to ensure all interface{} instances are replaced with any).contracts/ai/response.go (1)
5-17: Consider removingcontext.Contextfrom getter methods.The
context.Contextparameter onText(),Usage(),Raw(),InputTokens(),OutputTokens(), andTotalTokens()appears unnecessary. These methods typically return cached or computed values from an already-received response. Context is useful for cancellation and deadlines during I/O operations, but these getters should not perform any blocking work.If these methods are purely returning stored data, removing the context parameter would simplify the API:
type Response interface { Text() string Usage() Usage Raw() any } type Usage interface { InputTokens() int OutputTokens() int TotalTokens() int }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@contracts/ai/response.go` around lines 5 - 17, The getters on the Response and Usage interfaces unnecessarily take context.Context; remove the context parameter from Response.Text, Response.Usage, Response.Raw and from Usage.InputTokens, Usage.OutputTokens, Usage.TotalTokens, then update all implementing types and callers to the new no-context signatures (adjust receiver methods on concrete response/usage structs, update interface assertions and any places calling these methods), remove unused context imports where applicable, and run tests/build to ensure all call sites are updated.mocks/ai/Response.go (1)
27-70: Align the generatedRawhelpers with the contract’sanytype.Lines 27, 34, 53, 64, and 69 still emit
interface{}even thoughcontracts/ai/response.goalready exposesRaw(ctx context.Context) any. Since this file is generated, please fix this in the mockery template/config once so regenerating the AI mocks does not keep reintroducing the legacy spelling here and in the other new mock files.As per coding guidelines,
**/*.go: Useanyinstead ofinterface{}when writing Go code`.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mocks/ai/Response.go` around lines 27 - 70, The generated mock uses the old Go alias interface{} instead of any; update the mock to use any in the Response.Raw signature and all helper methods: change the return and parameter types for Response.Raw(ctx context.Context) (r0 any), the Response_Raw_Call.Return(_a0 any), the helper Raw(ctx interface{}) (you may keep the helper param type as interface{} if needed but ensure any is used where it represents the contract type), and RunAndReturn(run func(context.Context) any) so the types line up with contracts/ai/response.go; then fix the mockery template/config so future regenerations emit any instead of interface{} (adjust the template type mapping or generator flags that control Go 1.18+ alias usage).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@ai/application.go`:
- Around line 36-43: The WithContext method (Application.WithContext) should
guard against a nil ctx to avoid downstream panics: inside WithContext, check if
the provided ctx is nil and if so substitute context.Background() (or
context.TODO()) before constructing the new Application instance so the returned
AI always has a non-nil ctx; update the function to use that non-nil ctx when
setting the ctx field.
- Around line 46-47: The public method Application.Agent currently returns (nil,
nil) which signals success with a nil conversation and can cause nil
dereferences; update Application.Agent to return a non-nil error when the
operation is unimplemented or fails (e.g., an explicit "not implemented" or
appropriate domain error) instead of returning (nil, nil). Locate the
Application.Agent(func (r *Application) Agent(...)) implementation and replace
the nil-success return with a descriptive error return (keeping the conversation
nil) or implement the actual creation logic so callers never observe (nil, nil).
In `@ai/conversation.go`:
- Around line 22-24: The Prompt method on conversation currently returns nil,
nil and never delegates to the underlying provider; replace the no-op by
invoking the conversation's provider Prompt call (e.g.,
r.provider.Prompt(input)) and return its contractsai.Response and error, and if
r.provider is nil return a meaningful error instead of nil, nil so failures
aren't masked; update conversation.Prompt to forward input to the provider,
handle a nil r.provider (or wrap provider errors) and propagate the provider's
response and error up to the caller.
- Around line 26-31: The Conversation methods are missing implementations:
implement conversation.Messages() to return the conversation's message slice
(e.g. return r.messages) rather than nil, and implement conversation.Reset() to
clear that slice and any related state (e.g. set r.messages = nil or r.messages
= r.messages[:0] and reset any indexes/cursors/metadata on the conversation
struct); if the struct uses synchronization (e.g. r.mu or similar), perform
these reads/writes under the appropriate lock to avoid races.
In `@mocks/ai/Ai.go`:
- Around line 14-16: There is a duplicate type declaration for AI (type AI
struct { mock.Mock }) causing a redeclaration error; locate all declarations of
the AI mock (type name AI and any files in the mocks/ai package that define it),
remove the redundant declaration, and then regenerate mocks so only a single AI
mock type exists (ensure casing-consistent filenames to avoid duplicate
generation across different files).
In `@mocks/ai/AI.go`:
- Around line 1-159: Delete the duplicate autogenerated mock file named
mocks/ai/Ai.go (which collides with mocks/ai/AI.go) so only mocks/ai/AI.go
remains; ensure no other code imports or references the deleted file (tests
should reference the mock type AI and functions like NewAI, AI.Agent,
AI.WithContext from mocks/ai/AI.go) and run the test/build to confirm the
case-insensitive filename collision is resolved.
In `@mocks/ai/Option.go`:
- Around line 1-65: The generated mock incorrectly models the function type
Option as a struct with Execute (symbols: Option, Execute, NewOption); remove
this mock file entirely and update .mockery.yaml to exclude the Option function
(either remove it from generated list or set all: false and explicitly list
types), or if you must keep a mock-like helper, replace usages with a simple
function stub rather than testify/mock structs and use map[string]any (not
map[string]interface{}) where applicable; ensure no other code imports or
references mocks/ai/Option.go before deleting.
---
Nitpick comments:
In `@contracts/ai/response.go`:
- Around line 5-17: The getters on the Response and Usage interfaces
unnecessarily take context.Context; remove the context parameter from
Response.Text, Response.Usage, Response.Raw and from Usage.InputTokens,
Usage.OutputTokens, Usage.TotalTokens, then update all implementing types and
callers to the new no-context signatures (adjust receiver methods on concrete
response/usage structs, update interface assertions and any places calling these
methods), remove unused context imports where applicable, and run tests/build to
ensure all call sites are updated.
In `@mocks/ai/Ai.go`:
- Around line 28-33: The generated mocks use interface{} instead of Go's
preferred alias any; update the mock generation template/config so all
occurrences emit any (e.g., change make([]interface{}, len(options)) ->
make([]any, len(options)) and var _ca []interface{} -> var _ca []any) and then
regenerate the mocks (check files like mocks/ai/Ai.go, Ai.go symbols _va,
options, _ca, agent and other generated files such as AI.go, Provider.go,
Response.go, Usage.go to ensure all interface{} instances are replaced with
any).
In `@mocks/ai/Response.go`:
- Around line 27-70: The generated mock uses the old Go alias interface{}
instead of any; update the mock to use any in the Response.Raw signature and all
helper methods: change the return and parameter types for Response.Raw(ctx
context.Context) (r0 any), the Response_Raw_Call.Return(_a0 any), the helper
Raw(ctx interface{}) (you may keep the helper param type as interface{} if
needed but ensure any is used where it represents the contract type), and
RunAndReturn(run func(context.Context) any) so the types line up with
contracts/ai/response.go; then fix the mockery template/config so future
regenerations emit any instead of interface{} (adjust the template type mapping
or generator flags that control Go 1.18+ alias usage).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 03877a7b-5651-403a-b5a9-cd5a3a7437d9
📒 Files selected for processing (27)
AGENTS.mdai/application.goai/conversation.goai/option.goai/service_provider.gocontracts/ai/agent.gocontracts/ai/ai.gocontracts/ai/driver.gocontracts/ai/message.gocontracts/ai/option.gocontracts/ai/response.gocontracts/binding/binding.gocontracts/facades/facades.gocontracts/foundation/application.gofacades/facades.gofoundation/container.gomocks/ai/AI.gomocks/ai/Agent.gomocks/ai/Ai.gomocks/ai/Conversation.gomocks/ai/Option.gomocks/ai/Provider.gomocks/ai/ProviderCreator.gomocks/ai/Response.gomocks/ai/Usage.gomocks/foundation/Application.gosupport/convert/facade_test.go
|
One API thought: right now conv, err := facades.AI().
WithContext(ctx).
Agent(myAgent, ai.WithProvider("openai"), ai.WithModel("gpt-4o"))Could we move those up to conv, err := facades.AI().
WithContext(ctx).
WithProvider("openai").
WithModel("gpt-4o").
Agent(myAgent)Feels a bit cleaner/readable, and matches the idea that provider/model are usually conversation-level config rather than agent-scoped. If there’s a reason they need to stay per-agent, totally fine—just wanted to float the idea. |
@copilot Compare these two styles above. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1410 +/- ##
==========================================
- Coverage 67.28% 67.18% -0.10%
==========================================
Files 344 348 +4
Lines 25574 25609 +35
==========================================
- Hits 17208 17206 -2
- Misses 7638 7674 +36
- Partials 728 729 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@almas-x I think it's better that the provider and the model belong to agents instead of being set in advance. The normal process when using agents should be: Which agents should be used -> Which model is suitable. Not choose the model first, then agents. And the provider and the model can be set in the agent struct as well (haven't implemented in this PR). |
Yeah, that makes sense. If provider/model are meant to be agent-owned, pushing them down into the agent struct feels like the cleanest end state.👍 |
|
@hwbrzzl a couple of questions here: Why does And why is context pinned at conversation level via |
|
@krishankumar01 Good catch, updated. |
|
@hwbrzzl Couple more questions here: How are we handling streaming responses? Also how is a provider(default) configured? The examples show |
|
@krishankumar01 this PR only contains the basic code structure. I will add the first provider and the stream function in the following PRs. |
|
@goravel/core-developers could you give an approval if there are no questions? Thanks |
Overview
This PR introduces the AI module for Goravel — a provider-agnostic interface for building LLM-powered features in your application.
Features
contracts/ai— Public interfaces:AI,Conversation,Provider(driver),Agent,Response,Usage,Message,Optionai/— Core implementation:Application(manager),conversation, option helpers (WithProvider,WithModel,WithTimeout)ai/service_provider.go— Service provider wired into the IoC containermocks/ai/— Generated testify mocks for all AI interfacesfacades/— StaticAI()facade accessorExamples
1. Basic prompt
2. Choose a specific provider and model at call-time
3. Multi-turn conversation
4. Reset conversation history
5. Custom timeout
6. Implementing a custom Provider driver
Related
Closes #915
Summary by CodeRabbit