2121from mellea .backends import ModelOption
2222from mellea .backends .model_ids import IBM_GRANITE_4_HYBRID_MICRO
2323from mellea .backends .openai import OpenAIBackend
24- from mellea .backends .tools import MelleaTool , add_tools_from_context_actions
24+ from mellea .backends .tools import MelleaTool
2525from mellea .core import CBlock , Component , ModelOutputThunk , TemplateRepresentation
2626from mellea .core .base import AbstractMelleaTool
2727from mellea .formatters import TemplateFormatter
@@ -116,6 +116,7 @@ def format_for_llm(self) -> TemplateRepresentation | str:
116116 obj = self ,
117117 args = {"description" : "Database query interface" },
118118 tools = {"query" : MelleaTool .from_callable (self ._tool .run )},
119+ template = "🗄️ **Database Interface**: {{description}}\n Available: SQL query tool" ,
119120 )
120121
121122 def _parse (self , computed : ModelOutputThunk ) -> str :
@@ -139,6 +140,7 @@ def format_for_llm(self) -> TemplateRepresentation | str:
139140 obj = self ,
140141 args = {"description" : "Search interface" },
141142 tools = {"query" : MelleaTool .from_callable (self ._tool .run )},
143+ template = "🔍 **Search Interface**: {{description}}\n Available: Document search tool" ,
142144 )
143145
144146 def _parse (self , computed : ModelOutputThunk ) -> str :
@@ -147,34 +149,35 @@ def _parse(self, computed: ModelOutputThunk) -> str:
147149
148150
149151def main ():
150- """Demonstrate Pattern 2: Components in context + explicit tools ."""
152+ """Demonstrate Pattern 2: Components in context + auto tool extraction ."""
151153 print ("=" * 70 )
152- print ("PATTERN 2: Components in Context + Explicit Tools for Tool Calling" )
154+ print ("PATTERN 2: Components in Context + Auto Tool Extraction" )
155+ print ("=" * 70 )
156+
157+ print ("\n " + "=" * 70 )
158+ print ("STEP 1: Create Components" )
153159 print ("=" * 70 )
154160
155161 # Create components
156162 db_component = DatabaseComponent ()
157163 search_component = SearchComponent ()
164+ print ("✓ Created DatabaseComponent and SearchComponent with tools" )
158165
159166 print ("\n " + "=" * 70 )
160- print ("STEP 1: Extract Tools from Components " )
167+ print ("STEP 2: Add Components to Context " )
161168 print ("=" * 70 )
162169
163- # Extract tools from components
164- ctx_actions = [db_component , search_component ]
165- tools = {}
166- add_tools_from_context_actions (tools , ctx_actions )
167-
168- print ("\n Extracted tools with ID-based prefixes:" )
169- for tool_name in sorted (tools .keys ()):
170- if tool_name .startswith ("component_" ):
171- print (f" - { tool_name } " )
172-
173- query_tools = [k for k in tools if "query" in k ]
174- print (f"\n Total query tools: { len (query_tools )} " )
170+ # Add components to context (no templates needed - already defined!)
171+ print ("\n Adding components to context..." )
172+ db_component = DatabaseComponent ()
173+ search_component = SearchComponent ()
174+ session_ctx = ChatContext ()
175+ session_ctx = session_ctx .add (db_component )
176+ session_ctx = session_ctx .add (search_component )
177+ print (" ✓ Added both components to context" )
175178
176179 print ("\n " + "=" * 70 )
177- print ("STEP 2 : Set Up Backend and Session" )
180+ print ("STEP 3 : Set Up Backend and Session" )
178181 print ("=" * 70 )
179182
180183 ollama_host = os .environ .get ("OLLAMA_HOST" , "localhost:11434" )
@@ -189,32 +192,9 @@ def main():
189192 base_url = f"{ ollama_host } /v1" ,
190193 api_key = "ollama" ,
191194 )
192- session = MelleaSession (backend , ctx = ChatContext ())
193-
194- print ("\n Session created" )
195-
196- print ("\n " + "=" * 70 )
197- print ("STEP 3: Add Context for Additional Information" )
198- print ("=" * 70 )
199-
200- # Add context blocks to the session (for demonstration of Pattern 2)
201- # In a real scenario, components could be added here if they have templates
202- print ("\n Adding context information..." )
203- context_block = CBlock (
204- "Available systems:\n "
205- "1. Database: For querying user information\n "
206- "2. Search: For finding documentation"
207- )
208- session .ctx = session .ctx .add (context_block )
209- print (" ✓ Added context block" )
195+ session = MelleaSession (backend , ctx = session_ctx )
210196
211- # Verify items are in context
212- context_items = session .ctx .view_for_generation ()
213- print (f"\n Context items: { len (context_items ) if context_items else 0 } " )
214- if context_items :
215- for i , item in enumerate (context_items ):
216- if isinstance (item , CBlock ):
217- print (f" { i + 1 } . CBlock: { item .value [:40 ]} ..." )
197+ print ("\n Session created with components in context" )
218198
219199 print ("\n " + "=" * 70 )
220200 print ("STEP 4: LLM Generation with Tool Calling" )
@@ -228,17 +208,16 @@ def main():
228208 )
229209
230210 print (f"\n Prompt:\n { prompt } " )
231- print (f"\n Tools available: { sorted (tools .keys ())} " )
232211
233- # IMPORTANT: Must explicitly pass tools via ModelOption.TOOLS
212+ # IMPORTANT: NO ModelOption.TOOLS - backend auto-extracts from context!
234213 print ("\n Calling session.instruct() with:" )
235- print (f" - Components in context: { len (context_items ) if context_items else 0 } " )
236- print (f" - Tools via ModelOption.TOOLS: { sorted (tools .keys ())} " )
214+ print (" - Components in context: YES (database + search)" )
215+ print (" - ModelOption.TOOLS: NO (backend auto-extracts!)" )
216+ print (" - tool_calls: True" )
237217
238218 response = session .instruct (
239219 prompt ,
240220 model_options = {
241- ModelOption .TOOLS : tools , # ← EXPLICIT TOOLS REQUIRED
242221 ModelOption .TOOL_CHOICE : "auto" ,
243222 ModelOption .MAX_NEW_TOKENS : 1000 ,
244223 },
@@ -266,32 +245,30 @@ def main():
266245 print ("Pattern 2 Summary" )
267246 print ("=" * 70 )
268247 print ("""
269- PATTERN 2: Components in Context + Explicit Tools
248+ PATTERN 2: Components in Context + Auto Tool Extraction
270249
271250Approach:
272- 1. Create components with tools
273- 2. Extract tools: add_tools_from_context_actions()
274- 3. Create session with ChatContext
275- 4. Add components to context: session.ctx = session.ctx.add(component)
276- 5. Pass tools explicitly: ModelOption.TOOLS = tools
277- 6. Call session.instruct() with tool_calls=True
251+ 1. Create components with tools and templates
252+ 2. Create session with ChatContext
253+ 3. Add components to context: session.ctx = session.ctx.add(component)
254+ 4. Call session.instruct() with tool_calls=True (NO ModelOption.TOOLS!)
278255
279256Benefits:
280257 ✓ Components rendered in the prompt
281- ✓ Components' tools available for tool calling
258+ ✓ Components' tools automatically extracted and available
282259 ✓ ID-based prefixing prevents tool collisions
283260 ✓ Multi-turn stable (same instances = same IDs)
261+ ✓ No explicit tool passing needed
284262
285263Key Point:
286- Even though components are in context, tools MUST be explicitly passed
287- via ModelOption.TOOLS. This is intentional design - two separate concerns:
288- - Context rendering (what the LLM sees in the conversation)
289- - Tool availability (what tools the LLM can call)
264+ The backend automatically calls add_tools_from_context_actions() when
265+ tool_calls=True, extracting tools from all components in the context.
266+ This makes Pattern 2 truly implicit and elegant.
290267
291268When to Use Pattern 2:
292269 - You need components to appear in the conversation
293- - You also need their tools available for calling
294- - You want full control over tool availability
270+ - You want their tools available for calling automatically
271+ - You prefer implicit over explicit tool passing
295272 """ )
296273
297274 print ("=" * 70 )
0 commit comments