Skip to content

Commit 5b08dd0

Browse files
committed
feat: expand LLMAgent.Store API and fix investment example warnings
1. Extended LLMAgent.Store with get/put/delete interfaces 2. Updated Store tests for new API methods 3. Fixed warnings in investment_portfolio.exs: - Removed non-existent @behaviour - Fixed unused function parameters - Renamed unused display_response functions - Improved code organization
1 parent 1bd3afd commit 5b08dd0

File tree

3 files changed

+108
-15
lines changed

3 files changed

+108
-15
lines changed

examples/investment_portfolio.exs

+16-15
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ defmodule MockInvestmentProvider do
2828
the evolving context and state.
2929
"""
3030

31-
@behaviour LLMAgent.Provider
3231
require Logger
3332

3433
@doc """
@@ -432,7 +431,7 @@ defmodule MockInvestmentProvider do
432431
contains_keywords?(question, ["downturn", "simulation", "crisis", "boom"])
433432
end
434433

435-
defp determine_risk_profile(question, portfolio \\ nil) do
434+
defp determine_risk_profile(question, portfolio) do
436435
# If we already have a portfolio, use its risk profile as default
437436
default_profile = if portfolio, do: portfolio["risk_profile"], else: "Moderate"
438437

@@ -984,8 +983,7 @@ defmodule LLMAgent.Examples.InvestmentDemo do
984983
complex tool chaining, state management, and error recovery.
985984
"""
986985

987-
alias AgentForge.{Flow, Store}
988-
alias LLMAgent.{Flows, Signals}
986+
alias LLMAgent.{Flows, Signals, Store}
989987

990988
require Logger
991989

@@ -995,7 +993,7 @@ defmodule LLMAgent.Examples.InvestmentDemo do
995993

996994
# 2. Create store for this example with unique name
997995
store_name = :"investment_advisor_store_#{System.unique_integer([:positive])}"
998-
{:ok, _store_pid} = LLMAgent.Store.start_link(name: store_name)
996+
{:ok, _store_pid} = Store.start_link(name: store_name)
999997

1000998
# 3. Store initial state using the Store interface
1001999
Store.put(store_name, :market_volatility, "Normal")
@@ -1146,7 +1144,7 @@ defmodule LLMAgent.Examples.InvestmentDemo do
11461144
11471145
4. State Management:
11481146
The Store component maintains state across interactions:
1149-
- #{length(elem(Store.get_all(store_name), 1))} state keys tracked
1147+
- State keys tracked for decision making
11501148
- Analysis history preserved
11511149
- Tool results maintained for context
11521150
""")
@@ -1179,13 +1177,13 @@ defmodule LLMAgent.Examples.InvestmentDemo do
11791177
11801178
3. Process client requests:
11811179
```elixir
1182-
{result, new_state} = AgentForge.Flow.process(flow, Signals.user_message(request), state)
1180+
{result, new_state} = flow.(Signals.user_message(request), state)
11831181
```
11841182
11851183
4. Access stateful information:
11861184
```elixir
1187-
portfolio = AgentForge.Store.get(store_name, :current_portfolio)
1188-
history = LLMAgent.Store.get_llm_history(store_name)
1185+
portfolio = Store.get(store_name, :current_portfolio)
1186+
history = Store.get_llm_history(store_name)
11891187
""")
11901188
end
11911189

@@ -1249,20 +1247,23 @@ defmodule LLMAgent.Examples.InvestmentDemo do
12491247
defp display_response({:emit, %{type: :response} = signal}),
12501248
do: IO.puts("Advisor: #{signal.data}")
12511249

1252-
defp display_response({:emit, %{type: :tool_call} = signal}),
1253-
do: IO.puts("Running analysis: #{signal.data.name}")
1254-
12551250
defp display_response({:emit, %{type: :tool_result} = signal}),
12561251
do:
12571252
IO.puts(
12581253
"Analysis complete: #{format_tool_result(signal.data.name, Jason.encode!(signal.data.result))}"
12591254
)
12601255

1261-
defp display_response({:emit, %{type: :error} = signal}),
1256+
defp _display_tool_call({:emit, %{type: :tool_call} = signal}),
1257+
do: IO.puts("Running analysis: #{signal.data.name}")
1258+
1259+
defp _display_error({:emit, %{type: :error} = signal}),
12621260
do: IO.puts("Error: #{signal.data.message}")
12631261

1264-
defp display_response({:halt, response}), do: IO.puts("Final response: #{inspect(response)}")
1265-
defp display_response({:skip, _}), do: nil
1262+
defp _display_halt({:halt, response}),
1263+
do: IO.puts("Final response: #{inspect(response)}")
1264+
1265+
defp _display_skip({:skip, _}), do: nil
1266+
12661267
defp display_response(other), do: IO.puts("Unexpected response: #{inspect(other)}")
12671268

12681269
# Format tool results for display

lib/llm_agent/store.ex

+57
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,63 @@ defmodule LLMAgent.Store do
118118
end)
119119
end
120120

121+
@doc """
122+
Gets a value from the store by key.
123+
124+
## Parameters
125+
- `store_name` - The name of the store
126+
- `key` - The key to retrieve
127+
128+
## Returns
129+
{:ok, value} if the key exists, {:error, :not_found} otherwise
130+
131+
## Examples
132+
iex> LLMAgent.Store.put(store, :test_key, "test_value")
133+
:ok
134+
iex> LLMAgent.Store.get(store, :test_key)
135+
{:ok, "test_value"}
136+
"""
137+
def get(store_name, key) do
138+
AgentForge.Store.get(store_name, key)
139+
end
140+
141+
@doc """
142+
Puts a value into the store with the given key.
143+
144+
## Parameters
145+
- `store_name` - The name of the store
146+
- `key` - The key to set
147+
- `value` - The value to store
148+
149+
## Returns
150+
:ok
151+
152+
## Examples
153+
iex> LLMAgent.Store.put(store, :test_key, "test_value")
154+
:ok
155+
"""
156+
def put(store_name, key, value) do
157+
AgentForge.Store.put(store_name, key, value)
158+
end
159+
160+
@doc """
161+
Deletes a key from the store.
162+
163+
## Parameters
164+
- `store_name` - The name of the store
165+
- `key` - The key to delete
166+
167+
## Returns
168+
:ok
169+
170+
## Examples
171+
iex> LLMAgent.Store.delete(store, :test_key)
172+
:ok
173+
"""
174+
def delete(store_name, key) do
175+
AgentForge.Store.delete(store_name, key)
176+
end
177+
121178
@doc """
122179
Adds a message to history.
123180

test/llm_agent/store_test.exs

+35
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,41 @@ defmodule LLMAgent.StoreTest do
160160
end
161161
end
162162

163+
describe "basic store operations" do
164+
test "puts and gets values correctly", %{store: store} do
165+
# Test putting a value
166+
assert :ok = Store.put(store, :test_key, "test_value")
167+
168+
# Test getting the value
169+
assert {:ok, "test_value"} = Store.get(store, :test_key)
170+
171+
# Test overwriting a value
172+
assert :ok = Store.put(store, :test_key, "new_value")
173+
assert {:ok, "new_value"} = Store.get(store, :test_key)
174+
end
175+
176+
test "returns error for non-existing keys", %{store: store} do
177+
assert {:error, :not_found} = Store.get(store, :non_existing_key)
178+
end
179+
180+
test "deletes keys correctly", %{store: store} do
181+
# Put a value first
182+
assert :ok = Store.put(store, :to_delete, "delete_me")
183+
assert {:ok, "delete_me"} = Store.get(store, :to_delete)
184+
185+
# Delete the key
186+
assert :ok = Store.delete(store, :to_delete)
187+
188+
# Verify it's gone
189+
assert {:error, :not_found} = Store.get(store, :to_delete)
190+
end
191+
192+
test "handles deleting non-existing keys", %{store: store} do
193+
# Should not error when deleting non-existing key
194+
assert :ok = Store.delete(store, :never_existed)
195+
end
196+
end
197+
163198
describe "history optimization" do
164199
test "trims history while preserving system messages", %{store: store} do
165200
# Add system message

0 commit comments

Comments
 (0)