• bitcoinBitcoin(BTC)$60,091.001.55%
  • ethereumEthereum(ETH)$1,578.691.26%
  • tetherTether(USDT)$1.000.02%
  • binancecoinBNB(BNB)$564.262.19%
  • usd-coinUSDC(USDC)$1.000.00%
  • rippleXRP(XRP)$1.040.90%
  • solanaSolana(SOL)$71.438.54%
  • tronTRON(TRX)$0.319532-0.93%
  • Figure HelocFigure Heloc(FIGR_HELOC)$1.030.59%
  • HyperliquidHyperliquid(HYPE)$64.576.30%
  • dogecoinDogecoin(DOGE)$0.0745062.44%
  • RainRain(RAIN)$0.015704-0.43%
  • USDSUSDS(USDS)$1.00-0.01%
  • leo-tokenLEO Token(LEO)$9.28-0.65%
  • zcashZcash(ZEC)$410.153.86%
  • LABLAB(LAB)$19.276.15%
  • stellarStellar(XLM)$0.176734-0.21%
  • moneroMonero(XMR)$316.504.00%
  • CantonCanton(CC)$0.1498030.63%
  • whitebitWhiteBIT Coin(WBT)$48.601.88%
  • cardanoCardano(ADA)$0.1467673.27%
  • chainlinkChainlink(LINK)$7.302.05%
  • USD1USD1(USD1)$1.000.03%
  • daiDai(DAI)$1.000.02%
  • Ethena USDeEthena USDe(USDE)$1.00-0.01%
  • the-open-networkGram (prev. Toncoin)(GRAM)$1.560.59%
  • bitcoin-cashBitcoin Cash(BCH)$199.636.10%
  • litecoinLitecoin(LTC)$41.302.77%
  • hedera-hashgraphHedera(HBAR)$0.0732931.02%
  • Circle USYCCircle USYC(USYC)$1.13-0.01%
  • Global DollarGlobal Dollar(USDG)$1.000.01%
  • suiSui(SUI)$0.692.74%
  • paypal-usdPayPal USD(PYUSD)$1.000.04%
  • avalanche-2Avalanche(AVAX)$6.293.33%
  • crypto-com-chainCronos(CRO)$0.0546590.90%
  • tether-goldTether Gold(XAUT)$4,071.451.30%
  • shiba-inuShiba Inu(SHIB)$0.0000042.37%
  • BlackRock USD Institutional Digital Liquidity FundBlackRock USD Institutional Digital Liquidity Fund(BUIDL)$1.000.00%
  • nearNEAR Protocol(NEAR)$1.80-2.51%
  • Ondo US Dollar YieldOndo US Dollar Yield(USDY)$1.13-0.17%
  • BittensorBittensor(TAO)$212.290.44%
  • pax-goldPAX Gold(PAXG)$4,075.271.38%
  • World Liberty FinancialWorld Liberty Financial(WLFI)$0.057883-2.25%
  • uniswapUniswap(UNI)$2.943.68%
  • AsterAster(ASTER)$0.633.00%
  • worldcoin-wldWorldcoin(WLD)$0.461382-5.63%
  • Ripple USDRipple USD(RLUSD)$1.000.02%
  • okbOKB(OKB)$75.061.07%
  • OndoOndo(ONDO)$0.3130542.14%
  • HTX DAOHTX DAO(HTX)$0.000002-0.76%
TradePoint.io
  • Main
  • AI & Technology
  • Stock Charts
  • Market & News
  • Business
  • Finance Tips
  • Trade Tube
  • Blog
  • Shop
No Result
View All Result
TradePoint.io
No Result
View All Result

Build a Nanobot-Style AI Agent in Google Colab with Tool Calling, Session Memory, Skills, and MCP Servers

June 26, 2026
in AI & Technology
Reading Time: 5 mins read
A A
Build a Nanobot-Style AI Agent in Google Colab with Tool Calling, Session Memory, Skills, and MCP Servers
ShareShareShareShareShare

YOU MAY ALSO LIKE

Not All Tech Survives Solar Storms, Here’s What’s Most At Risk

Most companies think they’re building a software factory. They’re actually just shipping bugs faster.

import subprocess, sys
def _pip_install(*pkgs):
   try:
       subprocess.run([sys.executable, "-m", "pip", "install", "-q", *pkgs], check=True)
   except Exception as e:
       print(f"(pip install skipped/failed for {pkgs}: {e})")
_HAVE_OPENAI = False
try:
   import openai
   _HAVE_OPENAI = True
except Exception:
   _pip_install("openai>=1.0.0")
   try:
       import openai
       _HAVE_OPENAI = True
   except Exception:
       _HAVE_OPENAI = False
try:
   import nest_asyncio
   nest_asyncio.apply()
except Exception:
   try:
       _pip_install("nest_asyncio")
       import nest_asyncio
       nest_asyncio.apply()
   except Exception:
       pass
import os
import re
import json
import time
import math
import asyncio
import inspect
import textwrap
import contextlib
import io
from dataclasses import dataclass, field
from typing import Any, Callable, Optional, Awaitable, get_type_hints
def banner(title: str) -> None:
   line = "═" * 78
   print(f"\n{line}\n  {title}\n{line}")
@dataclass
class ToolCall:
   """A normalized request from the model to run one tool."""
   id: str
   name: str
   arguments: dict
@dataclass
class Usage:
   prompt_tokens: int = 0
   completion_tokens: int = 0
   @property
   def total(self) -> int:
       return self.prompt_tokens + self.completion_tokens
@dataclass
class LLMResponse:
   """The single shape every provider must return."""
   content: Optional[str]
   tool_calls: list[ToolCall] = field(default_factory=list)
   finish_reason: str = "stop"
   usage: Usage = field(default_factory=Usage)
class Provider:
   """Base class. A provider turns (messages, tools) into an LLMResponse."""
   name = "base"
   async def complete(self, messages: list[dict], tools: list[dict]) -> LLMResponse:
       raise NotImplementedError
class OpenAICompatibleProvider(Provider):
   """
   Works with OpenAI and every OpenAI-compatible gateway (OpenRouter, DeepSeek,
   Together, vLLM, LM Studio, Ollama's /v1, ...). This mirrors how nanobot speaks
   to most providers under the hood.
   """
   name = "openai-compatible"
   def __init__(self, api_key: str, model: str, base_url: Optional[str] = None):
       from openai import AsyncOpenAI
       self.model = model
       self.client = AsyncOpenAI(api_key=api_key, base_url=base_url)
   async def complete(self, messages: list[dict], tools: list[dict]) -> LLMResponse:
       kwargs: dict[str, Any] = {"model": self.model, "messages": messages}
       if tools:
           kwargs["tools"] = tools
           kwargs["tool_choice"] = "auto"
       resp = await self.client.chat.completions.create(**kwargs)
       choice = resp.choices[0]
       msg = choice.message
       calls: list[ToolCall] = []
       for tc in (msg.tool_calls or []):
           try:
               args = json.loads(tc.function.arguments or "{}")
           except json.JSONDecodeError:
               args = {"_raw": tc.function.arguments}
           calls.append(ToolCall(id=tc.id, name=tc.function.name, arguments=args))
       usage = Usage(
           prompt_tokens=getattr(resp.usage, "prompt_tokens", 0) or 0,
           completion_tokens=getattr(resp.usage, "completion_tokens", 0) or 0,
       )
       return LLMResponse(
           content=msg.content,
           tool_calls=calls,
           finish_reason=choice.finish_reason or "stop",
           usage=usage,
       )
class MockProvider(Provider):
   """
   A deterministic, rule-based "LLM" so this entire tutorial runs with NO API key
   and NO network — letting you watch the agent loop, tool calls, and memory work.
   It imitates the ONE thing that matters for the loop: deciding to emit a tool call
   (in the exact normalized shape a real model would) and then, once tool results
   come back, producing a final natural-language answer. The agent loop cannot tell
   it apart from OpenAI — that's the whole point of the provider contract.
   """
   name = "mock"
   def __init__(self, model: str = "mock-1"):
       self.model = model
   @staticmethod
   def _last_user_text(messages: list[dict]) -> str:
       for m in reversed(messages):
           if m.get("role") == "user":
               c = m.get("content")
               return c if isinstance(c, str) else json.dumps(c)
       return ""
   @staticmethod
   def _already_called(messages: list[dict], tool_name: str) -> bool:
       for m in messages:
           if m.get("role") == "assistant" and m.get("tool_calls"):
               for tc in m["tool_calls"]:
                   if tc["function"]["name"] == tool_name:
                       return True
       return False
   @staticmethod
   def _extract_math(text: str) -> str:
       """Pull the first math-looking chunk out of a sentence (mock-only helper)."""
       t = re.sub(r"square roots? of (\d+(?:\.\d+)?)", r"sqrt(\1)", text)
       t = t.replace("^", "**")
       pattern = (r"(?:sqrt\(\d+(?:\.\d+)?\)|\d+(?:\.\d+)?)"
                  r"(?:\s*(?:\*\*|[\+\-\*\/])\s*(?:sqrt\(\d+(?:\.\d+)?\)|\d+(?:\.\d+)?))*")
       m = re.search(pattern, t)
       return m.group(0).strip() if m else t.strip()
   @staticmethod
   def _scan_memory(messages: list[dict]) -> tuple[Optional[str], Optional[str]]:
       """Read back simple facts from prior USER turns — proves session memory is
       actually being fed to the model (mock-only convenience)."""
       name = love = None
       for m in messages:
           if m.get("role") == "user" and isinstance(m.get("content"), str):
               tx = m["content"].lower()
               nm = re.search(r"my name is (\w+)", tx)
               if nm:
                   name = nm.group(1).title()
               lv = re.search(r"i (?:love|like) (\w+)", tx)
               if lv:
                   love = lv.group(1).title()
       return name, love
   async def complete(self, messages: list[dict], tools: list[dict]) -> LLMResponse:
       await asyncio.sleep(0)
       user = self._last_user_text(messages).lower()
       tool_names = {t["function"]["name"] for t in tools}
       usage = Usage(prompt_tokens=sum(len(str(m)) for m in messages) // 4, completion_tokens=12)
       def call(name, args):
           return LLMResponse(
               content=None,
               tool_calls=[ToolCall(id=f"call_{name}_{int(time.time()*1000)%100000}",
                                    name=name, arguments=args)],
               finish_reason="tool_calls",
               usage=usage,
           )
       has_digit = bool(re.search(r"\d", user))
       wants_math = has_digit and (
           bool(re.search(r"[\+\-\*\/\^]", user)) or "sqrt" in user
           or "square root" in user
           or any(w in user for w in ["calculate", "compute", "evaluate", "what is", "what's"]))
       if "calculator" in tool_names and wants_math and not self._already_called(messages, "calculator"):
           return call("calculator", {"expression": self._extract_math(user)})
       if "get_current_time" in tool_names and not self._already_called(messages, "get_current_time"):
           if any(w in user for w in ["time", "date", "today", "now", "o'clock"]):
               tz = "UTC"
               m = re.search(r"in ([a-zA-Z_\/ ]+)", user)
               if m:
                   cand = m.group(1).strip().title().replace(" ", "_")
                   tz = {"Tokyo": "Asia/Tokyo", "Delhi": "Asia/Kolkata",
                         "New_York": "America/New_York", "London": "Europe/London"}.get(cand, cand)
               return call("get_current_time", {"timezone": tz})
       if "remember_fact" in tool_names and not self._already_called(messages, "remember_fact"):
           m = re.search(r"my favorite (?:programming )?language is (\w+)", user)
           if m:
               return call("remember_fact", {"key": "favorite_language", "value": m.group(1)})
       if "recall_fact" in tool_names and not self._already_called(messages, "recall_fact"):
           if any(w in user for w in ["my favorite", "do you remember", "recall", "what did i tell"]):
               key = "favorite_language" if "language" in user else "note"
               return call("recall_fact", {"key": key})
       if "run_python" in tool_names and not self._already_called(messages, "run_python"):
           py_kw = any(w in user for w in ["fibonacci", "prime", "factorial", "simulate"])
           py_action = "python" in user and any(
               w in user for w in ["run", "write", "code", "print", "execute", "snippet"])
           if py_kw or py_action:
               if "fibonacci" in user:
                   code = ("def fib(n):\n a,b=0,1\n out=[]\n"
                           " for _ in range(n):\n  out.append(a); a,b=b,a+b\n return out\n"
                           "print(fib(12))")
               elif "prime" in user:
                   code = ("primes=[n for n in range(2,50) "
                           "if all(n%d for d in range(2,int(n**0.5)+1))]\nprint(primes)")
               elif "factorial" in user:
                   code = "import math; print(math.factorial(10))"
               else:
                   code = "print(sum(range(1,101)))"
               return call("run_python", {"code": code})
       if "web_search" in tool_names and not self._already_called(messages, "web_search"):
           if any(w in user for w in ["search", "look up", "latest", "news about", "find information"]):
               return call("web_search", {"query": self._last_user_text(messages)})
       if any(p in user for p in ["my name", "who am i", "what do i love", "what i love"]):
           name, love = self._scan_memory(messages)
           bits = []
           if name:
               bits.append(f"your name is {name}")
           if love:
               bits.append(f"you love {love}")
           if bits:
               return LLMResponse(content="From our conversation, " + " and ".join(bits) + ".",
                                  tool_calls=[], finish_reason="stop", usage=usage)
       tool_outputs = [m["content"] for m in messages if m.get("role") == "tool"]
       if tool_outputs:
           joined = " ".join(tool_outputs)
           answer = f"Based on the tool results, here's what I found: {joined}"
       elif any(w in user for w in ["hello", "hi", "hey"]):
           answer = "Hello! I'm a mock nanobot agent. Ask me to calculate, tell time, run Python, or remember things."
       else:
           answer = ("[mock LLM] I would normally reason about this with a real model. "
                     "Set NANOBOT_API_KEY to use a live LLM. For now, try prompts with math, "
                     "time, Python, or memory so you can see the tool loop fire.")
       return LLMResponse(content=answer, tool_calls=[], finish_reason="stop", usage=usage)

Credit: Source link

ShareTweetSendSharePin

Related Posts

Not All Tech Survives Solar Storms, Here’s What’s Most At Risk
AI & Technology

Not All Tech Survives Solar Storms, Here’s What’s Most At Risk

June 26, 2026
Most companies think they’re building a software factory. They’re actually just shipping bugs faster.
AI & Technology

Most companies think they’re building a software factory. They’re actually just shipping bugs faster.

June 26, 2026
‘Careless People’ Author Accuses Meta Of ‘Punishing’ Whistleblower
AI & Technology

‘Careless People’ Author Accuses Meta Of ‘Punishing’ Whistleblower

June 26, 2026
Meet container: Apple’s Open-Source Swift Tool for Running Linux Containers as Lightweight VMs on Apple Silicon
AI & Technology

Meet container: Apple’s Open-Source Swift Tool for Running Linux Containers as Lightweight VMs on Apple Silicon

June 26, 2026
Next Post
World Cup kicks off in the iconic Azteca Stadium in Mexico City

World Cup kicks off in the iconic Azteca Stadium in Mexico City

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Search

No Result
View All Result
Protect My House from My Husband’s Bad Decisions?

Protect My House from My Husband’s Bad Decisions?

June 22, 2026
Mexico opens historic World Cup with a win

Mexico opens historic World Cup with a win

June 26, 2026
Netflix’s New Horror Game Taps Into Your Phone For Deeper Immersion

Netflix’s New Horror Game Taps Into Your Phone For Deeper Immersion

June 23, 2026

About

Learn more

Our Services

Legal

Privacy Policy

Terms of Use

Bloggers

Learn more

Article Links

Contact

Advertise

Ask us anything

©2020- TradePoint.io - All rights reserved!

Tradepoint.io, being just a publishing and technology platform, is not a registered broker-dealer or investment adviser. So we do not provide investment advice. Rather, brokerage services are provided to clients of Tradepoint.io by independent SEC-registered broker-dealers and members of FINRA/SIPC. Every form of investing carries some risk and past performance is not a guarantee of future results. “Tradepoint.io“, “Instant Investing” and “My Trading Tools” are registered trademarks of Apperbuild, LLC.

This website is operated by Apperbuild, LLC. We have no link to any brokerage firm and we do not provide investment advice. Every information and resource we provide is solely for the education of our readers. © 2020 Apperbuild, LLC. All rights reserved.

No Result
View All Result
  • Main
  • AI & Technology
  • Stock Charts
  • Market & News
  • Business
  • Finance Tips
  • Trade Tube
  • Blog
  • Shop

© 2023 - TradePoint.io - All Rights Reserved!