Last active
July 20, 2026 19:45
-
-
Save ddh0/88c25e4e8e3a321fe997a48b2189c67f to your computer and use it in GitHub Desktop.
single-file example HTTP MCP tool server in Python 3.12.11 (for use with `llama-ui` or any other MCP-capable inference client)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # serve.py | |
| # Python 3.12.11 | |
| import os | |
| import re | |
| import sys | |
| import math | |
| import datetime | |
| from typing import Optional, Literal | |
| from dataclasses import dataclass | |
| from urllib.parse import quote, unquote | |
| # NEEDS PACKAGES: run this command: | |
| # `pip install fastmcp numpy requests beautifulsoup4 lxml curl_cffi>=0.6.0 trafilatura>=1.6.0` | |
| import numpy as np | |
| import requests as _requests | |
| from fastmcp import FastMCP | |
| from bs4 import BeautifulSoup | |
| try: | |
| from curl_cffi import requests as _curl_requests | |
| HAS_CURL_CFFI = True | |
| except ImportError: | |
| HAS_CURL_CFFI = False | |
| try: | |
| import trafilatura as _trafilatura | |
| HAS_TRAFILATURA = True | |
| except ImportError: | |
| HAS_TRAFILATURA = False | |
| serve = FastMCP(name="Local MCP Server (Python Tools)") | |
| # | |
| # Constants | |
| # | |
| _N_PREFETCH = 256 | |
| _DTYPE_NP = np.float32 | |
| _DDG_ENDPOINT = "https://html.duckduckgo.com/html/" | |
| _LEGACY_UA = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" | |
| class PowersOfTwo: | |
| """On-demand cached sequence of powers of two.""" | |
| def _compute_to(self, n: int) -> None: | |
| while self._computed_through < n: | |
| self._values.append(self._values[-1] * 2) | |
| self._computed_through += 1 | |
| def __getitem__(self, n: int) -> int: | |
| if not isinstance(n, int): | |
| raise TypeError("index must be a non-negative integer") | |
| if n < 0: | |
| raise IndexError("negative indices not supported") | |
| self._compute_to(n) | |
| return self._values[n] | |
| def __iter__(self): | |
| index = 0 | |
| while True: | |
| yield self[index] | |
| index += 1 | |
| def get(self, n: int) -> int: | |
| """Explicit getter; same as indexing.""" | |
| return self[n] | |
| def __init__(self): | |
| self._values: list[int] = [1] | |
| self._computed_through: int = 0 | |
| self._compute_to(_N_PREFETCH) | |
| POWERS = PowersOfTwo() | |
| class Primes: | |
| """On-demand cached sequence of prime numbers.""" | |
| def _compute_to(self, n: int) -> None: | |
| while self._computed_through < n: | |
| candidate = self._candidate | |
| if all(candidate % p != 0 for p in self._primes): | |
| self._primes.append(candidate) | |
| self._computed_through += 1 | |
| self._candidate += 1 | |
| def __getitem__(self, n: int) -> int: | |
| if not isinstance(n, int): | |
| raise TypeError("index must be a non-negative integer") | |
| if n < 0: | |
| raise IndexError("negative indices not supported") | |
| self._compute_to(n) | |
| return self._primes[n] | |
| def get(self, n: int) -> int: | |
| """Explicit getter; same as indexing.""" | |
| return self[n] | |
| def __iter__(self): | |
| index = 0 | |
| while True: | |
| yield self[index] | |
| index += 1 | |
| def __init__(self): | |
| self._primes: list[int] = [] | |
| self._computed_through: int = -1 | |
| self._candidate: int = 2 | |
| self._compute_to(_N_PREFETCH) | |
| PRIMES = Primes() | |
| @serve.tool | |
| def add(a: int | float, b: int | float) -> int | float: | |
| """Return the sum of `a` and `b`.""" | |
| return a + b | |
| @serve.tool | |
| def sub(a: int | float, b: int | float) -> int | float: | |
| """Return the difference of `a` minus `b`.""" | |
| return a - b | |
| @serve.tool | |
| def mul(a: int | float, b: int | float) -> int | float: | |
| """Return the product of `a` and `b`.""" | |
| return a * b | |
| @serve.tool | |
| def div(a: int | float, b: int | float) -> float: | |
| """Return the quotient of `a` divided by `b`. Raises ZeroDivisionError if `b` is zero.""" | |
| if b == 0: | |
| raise ZeroDivisionError("division by zero") | |
| return a / b | |
| @serve.tool | |
| def log(n: int | float) -> int | float: | |
| """Return the natural logarithim of `n`.""" | |
| return math.log(n) | |
| @serve.tool | |
| def log2(n: int | float) -> int | float: | |
| """Return the binary logarithim of `n`.""" | |
| return math.log2(n) | |
| @serve.tool | |
| def log10(n: int | float) -> int | float: | |
| """Return the base-10 logarithim of `n`.""" | |
| return math.log10(n) | |
| @serve.tool | |
| def sqrt(n: int | float) -> int | float: | |
| """Return the square root of `n`.""" | |
| return math.sqrt(n) | |
| @serve.tool | |
| def isqrt(n: int | float) -> int: | |
| """Return the integer square root of `n`.""" | |
| return math.isqrt(n) | |
| @serve.tool | |
| def sin(n: int | float) -> int | float: | |
| """Return the sine of `n`.""" | |
| return math.sin(n) | |
| @serve.tool | |
| def cos(n: int | float) -> int | float: | |
| """Return the cosine of `n`.""" | |
| return math.cos(n) | |
| @serve.tool | |
| def tan(n: int | float) -> int | float: | |
| """Return the tangent of `n`.""" | |
| return math.tan(n) | |
| @serve.tool | |
| def sinh(n: int | float) -> int | float: | |
| """Return the hyperbolic sine of `n`.""" | |
| return math.sinh(n) | |
| @serve.tool | |
| def cosh(n: int | float) -> int | float: | |
| """Return the hyperbolic cosine of `n`.""" | |
| return math.cosh(n) | |
| @serve.tool | |
| def tanh(n: int | float) -> int | float: | |
| """Return the hyperbolic tangent of `n`.""" | |
| return math.tanh(n) | |
| @serve.tool | |
| def exp(n: int | float) -> int | float: | |
| """Return `e^n`.""" | |
| return math.exp(n) | |
| @serve.tool | |
| def erf(n: int | float) -> int | float: | |
| """Return the result of the error function with parameter `n`.""" | |
| return math.erf(n) | |
| @serve.tool | |
| def factorial(n: int | float) -> int | float: | |
| """Return the factorial of a number `n`.""" | |
| return math.factorial(n) | |
| @serve.tool | |
| def is_prime(n: int) -> bool: | |
| """Return whether `n` is a prime number. Uses trial division up to sqrt(n), checking | |
| divisibility by 2, then only odd numbers. Leverages the cached `PRIMES` sequence for | |
| efficient repeated calls.""" | |
| if not isinstance(n, int): | |
| raise TypeError("n must be an integer") | |
| n = abs(n) | |
| if n < 2: | |
| return False | |
| if n < 4: # 2, 3 | |
| return True | |
| if n % 2 == 0: | |
| return False | |
| limit = math.isqrt(n) | |
| # check against cached primes first | |
| idx = 0 | |
| while True: | |
| try: | |
| p = PRIMES[idx] | |
| except IndexError: | |
| # PRIMES shouldn't raise IndexError (infinite iterator), but be safe | |
| break | |
| if p > limit: | |
| break | |
| if n % p == 0: | |
| return False | |
| idx += 1 | |
| # fallback: if PRIMES hasn't computed far enough, compute manually | |
| if idx >= _N_PREFETCH and p < limit: | |
| # iterate odd numbers from last cached prime + 2 up to limit | |
| candidate = PRIMES[_N_PREFETCH - 1] + 2 | |
| while candidate <= limit: | |
| if n % candidate == 0: | |
| return False | |
| candidate += 2 | |
| break | |
| return True | |
| @serve.tool | |
| def eval_expr(expression: str) -> object: | |
| """Evaluate a mathematical expression string and return the result. | |
| Provides a namespace with `math` module functions and common constants | |
| (pi, e, tau, inf, nan) for convenience. | |
| Args: | |
| expression: A string containing a valid Python expression, e.g. | |
| "sqrt(3**2 + 4**2)" or "sin(pi/4)". | |
| Returns: | |
| The result of evaluating the expression. | |
| Raises: | |
| Various exceptions on invalid expressions, undefined names, etc.""" | |
| import math as _math | |
| # build a safe-ish namespace with common math bindings | |
| _namespace = { | |
| # constants | |
| "pi": _math.pi, | |
| "e": _math.e, | |
| "tau": _math.tau, | |
| "inf": float("inf"), | |
| "nan": float("nan"), | |
| "True": True, | |
| "False": False, | |
| # math module functions | |
| "sqrt": _math.sqrt, | |
| "isqrt": _math.isqrt, | |
| "log": _math.log, | |
| "log2": _math.log2, | |
| "log10": _math.log10, | |
| "exp": _math.exp, | |
| "sin": _math.sin, | |
| "cos": _math.cos, | |
| "tan": _math.tan, | |
| "asin": _math.asin, | |
| "acos": _math.acos, | |
| "atan": _math.atan, | |
| "atan2": _math.atan2, | |
| "sinh": _math.sinh, | |
| "cosh": _math.cosh, | |
| "tanh": _math.tanh, | |
| "asinh": _math.asinh, | |
| "acosh": _math.acosh, | |
| "atanh": _math.atanh, | |
| "degrees": _math.degrees, | |
| "radians": _math.radians, | |
| "floor": _math.floor, | |
| "ceil": _math.ceil, | |
| "trunc": _math.trunc, | |
| "factorial": _math.factorial, | |
| "comb": _math.comb, | |
| "perm": _math.perm, | |
| "gcd": _math.gcd, | |
| "lcm": _math.lcm, | |
| "fabs": _math.fabs, | |
| "fmod": _math.fmod, | |
| "modf": _math.modf, | |
| "frexp": _math.frexp, | |
| "ldexp": _math.ldexp, | |
| "pow": _math.pow, | |
| "prod": _math.prod, | |
| "dist": _math.dist, | |
| "hypot": _math.hypot, | |
| "erf": _math.erf, | |
| "erfc": _math.erfc, | |
| "gamma": _math.gamma, | |
| "lgamma": _math.lgamma, | |
| # builtins (limited set) | |
| "abs": abs, | |
| "min": min, | |
| "max": max, | |
| "round": round, | |
| "sum": sum, | |
| "int": int, | |
| "float": float, | |
| "complex": complex, | |
| "bool": bool, | |
| "len": len, | |
| "range": range, | |
| "list": list, | |
| "tuple": tuple, | |
| "dict": dict, | |
| "set": set, | |
| "sorted": sorted, | |
| "reversed": reversed, | |
| "enumerate": enumerate, | |
| "zip": zip, | |
| "map": map, | |
| "filter": filter, | |
| "any": any, | |
| "all": all, | |
| "isinstance": isinstance, | |
| "type": type, | |
| "str": str, | |
| "repr": repr, | |
| "hex": hex, | |
| "oct": oct, | |
| "bin": bin, | |
| "ord": ord, | |
| "chr": chr, | |
| } | |
| try: | |
| result = eval(expression, {"__builtins__": {}}, _namespace) | |
| return result | |
| except Exception as e: | |
| # re-raise with a clear message | |
| raise type(e)(f"eval_expr: error evaluating '{expression}': {e}") from e | |
| # | |
| # ANSI codes (primarily used in the logger) | |
| # | |
| class ANSI: | |
| """ANSI codes for terminal emulators""" | |
| # standard colors | |
| FG_BLACK = '\x1b[30m' | |
| FG_RED = '\x1b[31m' | |
| FG_GREEN = '\x1b[32m' | |
| FG_YELLOW = '\x1b[33m' | |
| FG_BLUE = '\x1b[34m' | |
| FG_MAGENTA = '\x1b[35m' | |
| FG_CYAN = '\x1b[36m' | |
| FG_WHITE = '\x1b[37m' | |
| BG_BLACK = '\x1b[40m' | |
| BG_RED = '\x1b[41m' | |
| BG_GREEN = '\x1b[42m' | |
| BG_YELLOW = '\x1b[43m' | |
| BG_BLUE = '\x1b[44m' | |
| BG_MAGENTA = '\x1b[45m' | |
| BG_CYAN = '\x1b[46m' | |
| BG_WHITE = '\x1b[47m' | |
| # bright colors | |
| FG_BRIGHT_BLACK = '\x1b[90m' | |
| FG_BRIGHT_RED = '\x1b[91m' | |
| FG_BRIGHT_GREEN = '\x1b[92m' | |
| FG_BRIGHT_YELLOW = '\x1b[93m' | |
| FG_BRIGHT_BLUE = '\x1b[94m' | |
| FG_BRIGHT_MAGENTA = '\x1b[95m' | |
| FG_BRIGHT_CYAN = '\x1b[96m' | |
| FG_BRIGHT_WHITE = '\x1b[97m' | |
| BG_BRIGHT_BLACK = '\x1b[100m' | |
| BG_BRIGHT_RED = '\x1b[101m' | |
| BG_BRIGHT_GREEN = '\x1b[102m' | |
| BG_BRIGHT_YELLOW = '\x1b[103m' | |
| BG_BRIGHT_BLUE = '\x1b[104m' | |
| BG_BRIGHT_MAGENTA = '\x1b[105m' | |
| BG_BRIGHT_CYAN = '\x1b[106m' | |
| BG_BRIGHT_WHITE = '\x1b[107m' | |
| # text modes | |
| MODE_RESET_ALL = '\x1b[0m' | |
| MODE_BOLD_SET = '\x1b[1m' | |
| MODE_DIM_SET = '\x1b[2m' | |
| MODE_ITALIC_SET = '\x1b[3m' | |
| MODE_UNDERLINE_SET = '\x1b[4m' | |
| MODE_BLINKING_SET = '\x1b[5m' | |
| MODE_REVERSE_SET = '\x1b[7m' | |
| MODE_HIDDEN_SET = '\x1b[8m' | |
| MODE_STRIKETHROUGH_SET = '\x1b[9m' | |
| MODE_BOLD_RESET = '\x1b[22m' | |
| MODE_DIM_RESET = '\x1b[22m' | |
| MODE_ITALIC_RESET = '\x1b[23m' | |
| MODE_UNDERLINE_RESET = '\x1b[24m' | |
| MODE_BLINKING_RESET = '\x1b[25m' | |
| MODE_REVERSE_RESET = '\x1b[27m' | |
| MODE_HIDDEN_RESET = '\x1b[28m' | |
| MODE_STRIKETHROUGH_RESET = '\x1b[29m' | |
| # special | |
| BELL = '\a' | |
| TERMINAL_RESET = '\x1bc' | |
| SCROLLBACK_CLEAR = '\x1b[3J' | |
| CLEAR = TERMINAL_RESET + SCROLLBACK_CLEAR + MODE_RESET_ALL | |
| # | |
| # Logger | |
| # | |
| class Logger: | |
| _LogLvl = Literal[0,1,2,3] | |
| @staticmethod | |
| def _get_debug_lvl() -> _LogLvl: | |
| return 0 | |
| @staticmethod | |
| def _get_info_lvl() -> _LogLvl: | |
| return 1 | |
| @staticmethod | |
| def _get_warn_lvl() -> _LogLvl: | |
| return 2 | |
| @staticmethod | |
| def _get_error_lvl() -> _LogLvl: | |
| return 3 | |
| @staticmethod | |
| def _islvl(obj: object) -> bool: | |
| """Is this a valid log level?""" | |
| return obj in [0,1,2,3] | |
| @staticmethod | |
| def _get_prefix(lvl: _LogLvl) -> str: | |
| if lvl == 0: | |
| lvltxt = f"{ANSI.FG_BRIGHT_WHITE} DEBUG" | |
| elif lvl == 1: | |
| lvltxt = f"{ANSI.FG_BRIGHT_GREEN} INFO" | |
| elif lvl == 2: | |
| lvltxt = f"{ANSI.FG_BRIGHT_YELLOW}WARNING" | |
| elif lvl == 3: | |
| lvltxt = f"{ANSI.FG_BRIGHT_RED} ERROR" | |
| else: | |
| raise ValueError | |
| return ( | |
| f"{ANSI.MODE_RESET_ALL}{ANSI.MODE_BOLD_SET}{ANSI.FG_BRIGHT_BLACK}" | |
| f"{timestamp()}{ANSI.MODE_RESET_ALL}{ANSI.MODE_BOLD_SET} {lvltxt}" | |
| f"{ANSI.MODE_RESET_ALL}{ANSI.MODE_BOLD_SET}{ANSI.FG_BRIGHT_BLACK}:" | |
| f"{ANSI.MODE_RESET_ALL}" | |
| ) | |
| def __init__( | |
| self, | |
| stdout: Optional[object] = None, | |
| stderr: Optional[object] = None | |
| ): | |
| self.stdout = stdout if stdout is not None else sys.stdout | |
| self.stderr = stderr if stderr is not None else sys.stderr | |
| self.last_lvl: __class__._LogLvl = 1 | |
| def __call__(self, lvl: _LogLvl, msg: str) -> None: | |
| _lvl = lvl if __class__._islvl(lvl) else self.last_lvl | |
| out = self.stdout if _lvl in [0,1] else self.stderr | |
| print(f"{self._get_prefix(_lvl)} {msg}", file=out) | |
| self.last_lvl = _lvl | |
| def debug(self, msg: str) -> None: | |
| self(0, msg) | |
| def info(self, msg: str) -> None: | |
| self(1, msg) | |
| def warn(self, msg: str) -> None: | |
| self(2, msg) | |
| def error(self, msg: str) -> None: | |
| self(3, msg) | |
| def get_str(self, lvl: _LogLvl, msg: str) -> str: | |
| return f"{self._get_prefix(lvl if __class__._islvl(lvl) else self.last_lvl)} {msg}" | |
| _log = Logger() | |
| # | |
| # Utils | |
| # | |
| @serve.tool | |
| def timestamp() -> str: | |
| """Return a text-based timestamp representing the current time.""" | |
| return datetime.datetime.now().strftime("[%Y-%m-%d %a %H:%M:%S]") | |
| def isnum(obj: object) -> bool: | |
| return not isinstance(obj, bool) and isinstance(obj, int | float) | |
| def exception_to_str(exc: Exception) -> str: | |
| """ExceptionType: Exception message""" | |
| return f"{type(exc).__name__}: {exc}" | |
| def clear() -> None: | |
| """Clear the terminal""" | |
| print(ANSI.CLEAR, end='', flush=True) | |
| def cls() -> None: # same as `clear()` for convenience | |
| """Clear the terminal""" | |
| print(ANSI.CLEAR, end='', flush=True) | |
| # | |
| # Math functions | |
| # | |
| @serve.tool | |
| def softmax(z: list[float | int], T: float = 1.0, axis: int = -1) -> np.ndarray: | |
| """Numerically stable softmax over a specified axis of an array. | |
| Supports temperature scaling by parameter `T`.""" | |
| z_arr = np.array(z, dtype=_DTYPE_NP) | |
| if z_arr.size == 0: | |
| return z_arr | |
| if T == 0.0: | |
| result = np.zeros_like(z_arr) | |
| indices = np.argmax(z_arr, axis=axis) | |
| np.put_along_axis(result, np.expand_dims(indices, axis), 1.0, axis=axis) | |
| return result | |
| if T < 0: | |
| z_arr = -z_arr | |
| T = -T | |
| max_val = np.max(z_arr, axis=axis, keepdims=True) | |
| exp_vals = np.exp((z_arr - max_val) / T) | |
| return exp_vals / np.sum(exp_vals, axis=axis, keepdims=True) | |
| @serve.tool | |
| def softmax_n(z: list[float | int], n: int, T: float = 1.0, axis: int = -1) -> np.ndarray: | |
| """Apply softmax `n` times.""" | |
| for _ in range(n): | |
| z = softmax(z=z, T=T, axis=axis) | |
| return z | |
| @serve.tool | |
| def GEOMEAN(a: float | int, b: float | int) -> float: | |
| """Return the geometric mean of two numbers.""" | |
| if not (isnum(a) and isnum(b)): | |
| raise TypeError | |
| if a == 0 or b == 0: | |
| return 0.0 | |
| return math.exp((math.log(a) + math.log(b)) * 0.5) | |
| @serve.tool | |
| def geomean(arr: list[float | int]) -> float: | |
| """Calculates the geometric mean of an array.""" | |
| if not isinstance(arr, np.ndarray): | |
| arr = np.array(arr, dtype=_DTYPE_NP) | |
| else: | |
| arr = arr.astype(_DTYPE_NP) | |
| if arr.size == 0: | |
| raise ValueError('array size is 0') | |
| if np.any(arr < 0): | |
| return _DTYPE_NP(np.nan) | |
| if np.any(arr == 0): | |
| return _DTYPE_NP(0.0) | |
| return float(np.exp(np.mean(np.log(arr)))) | |
| @serve.tool | |
| def factors(n: int) -> list[int]: | |
| """Return the list of all factors of the absolute value of an integer `n`.""" | |
| if not isinstance(n, int): | |
| raise TypeError | |
| _n = abs(n) | |
| factors: set[int] = set() | |
| for i in range(1, math.isqrt(_n) + 1): | |
| if _n % i == 0: | |
| factors.add(i) | |
| factors.add(_n // i) | |
| return sorted(factors) | |
| @serve.tool | |
| def GCD(a: int, b: int): | |
| """Return the greatest common divisor of two integers using an optimized Euclidean algorithm.""" | |
| a = abs(a) | |
| b = abs(b) | |
| while (b != 0): | |
| a, b = b, a % b | |
| return a | |
| @serve.tool | |
| def LCM(a: int, b: int) -> int: | |
| """Return the least common multiple of two integers using the GCD relationship.""" | |
| if a == 0 or b == 0: | |
| return 0 | |
| return abs(a) // GCD(a, b) * abs(b) | |
| @serve.tool | |
| def gcd(nums: list[int]) -> int: | |
| """Return the greatest common divisor of a list of integers by iteratively applying `GCD()`.""" | |
| if not nums: | |
| raise ValueError("nums is empty") | |
| if len(nums) == 1: | |
| return abs(nums[0]) | |
| res = abs(nums[0]) | |
| for i in range(1, len(nums)): | |
| res = GCD(res, abs(nums[i])) | |
| return res | |
| @serve.tool | |
| def lcm(nums: list[int]) -> int: | |
| """Return the least common multiple of a list of integers by iteratively applying `LCM()`.""" | |
| if not nums: | |
| raise ValueError("nums is empty") | |
| res = abs(nums[0]) | |
| for i in range(1, len(nums)): | |
| res = LCM(res, abs(nums[i])) | |
| return res | |
| @serve.tool | |
| def cofactors(nums: list[int]) -> list[int]: | |
| """Return the list of factors common to all integers in the given list.""" | |
| if not nums: | |
| return [] | |
| return factors(gcd(nums)) | |
| @serve.tool | |
| def surprisal(p: float, base: Optional[int | float] = None) -> float: | |
| """Given a random event with probability `p`, calculate the surprisal | |
| (Shannon information content), as measured in nats (base `e`) by default, | |
| or as measured using the specified `base`.""" | |
| if p > 1.0: | |
| _log.warn(f"p = {p} > 1.0") | |
| return log(1 / p, base) if isnum(base) else log(1 / p) | |
| @serve.tool | |
| def percent(a: int | float, b: int | float) -> float: | |
| """What percent is `a` of `b`?""" | |
| return (a / b) * 100 | |
| @serve.tool | |
| def uniform(size: int, low: int | float, high: int | float) -> list: | |
| """Return a uniform random distribution of floats in the range [low, high) of a given size.""" | |
| if size < 0: | |
| raise ValueError("size must be non-negative") | |
| return np.random.uniform(low, high, size).astype(_DTYPE_NP).tolist() | |
| @serve.tool | |
| def binary_decomposition(n: int) -> list[int]: | |
| """Return a list of integers representing the binary decomposition (factorization) of an integer. | |
| Examples: | |
| `binary_decomposition(0)` -> `[]` because == 0 | |
| `binary_decomposition(1)` -> `[0]` because 2^0 == 1 | |
| `binary_decomposition(4)` -> `[2]` because 2^2 == 4 | |
| `binary_decomposition(9)` -> `[3, 0]` because 2^3 + 2^0 == 9 | |
| `binary_decomposition(100)` -> `[6, 5, 2]` because 2^6 + 2^5 + 2^2 == 100 | |
| `binary_decomposition(81920)` -> `[16, 14]` because 2^16 + 2^14 == 81920""" | |
| if not isinstance(n, int): | |
| raise TypeError("only supports integer input") | |
| if n < 0: | |
| raise ValueError("does not support negative input") | |
| decomp = [] | |
| if n == 0: | |
| return decomp | |
| p = 0 | |
| while n > 0: | |
| if n % 2: | |
| decomp.append(p) | |
| n //= 2 | |
| p += 1 | |
| return decomp[::-1] | |
| @serve.tool | |
| def binary_composition(nums: list[int]) -> int: | |
| """Reconstruct an integer from its binary decomposition (inverse of `binary_decomposition`). | |
| Given a list of exponents representing powers of two, returns their sum. | |
| Examples: | |
| `binary_composition([])` -> `0` because 0 | |
| `binary_composition([0])` -> `1` because 2^0 | |
| `binary_composition([2])` -> `4` because 2^2 | |
| `binary_composition([3, 0])` -> `9` because 2^3 + 2^0 | |
| `binary_composition([6, 5, 2])` -> `100` because 2^6 + 2^5 + 2^2""" | |
| res = 0 | |
| for n in nums: | |
| res += 2**n | |
| return res | |
| @serve.tool | |
| def decomposition(n: int, base: int) -> list[int]: | |
| """Return the decomposition of a non-negative integer `n` into powers of a given `base`. | |
| Each element in the returned list is an exponent such that the sum of `base^exponent` | |
| over all elements equals `n`. This is a generalisation of `binary_decomposition` | |
| to arbitrary bases. | |
| Examples: | |
| `decomposition(0, 2)` -> `[]` because 0 | |
| `decomposition(5, 2)` -> `[2, 0]` because 2^2 + 2^0 == 5 | |
| `decomposition(5, 3)` -> `[1, 1, 0]` because 3^1 + 3^1 + 3^0 == 5 | |
| `decomposition(42, 10)` -> `[1, 0, 0, 0]` because 10^1 + 10^0 + 10^0 + 10^0 == 42""" | |
| if not isinstance(n, int): | |
| raise TypeError("n must be an integer") | |
| if n < 0: | |
| raise ValueError("n must be non-negative") | |
| if not isinstance(base, int) or base < 2: | |
| raise ValueError("base must be an integer >= 2") | |
| decomp = [] | |
| if n == 0: | |
| return decomp | |
| exp = 0 | |
| while n > 0: | |
| n, digit = divmod(n, base) | |
| decomp.extend([exp] * digit) | |
| exp += 1 | |
| return decomp[::-1] | |
| # | |
| # Misc. functions | |
| # | |
| @serve.tool | |
| def random_hex(n: int) -> str: | |
| """Generates a random hexadecimal string representing `n` bytes.""" | |
| if not isinstance(n, int) or n <= 0: | |
| raise ValueError | |
| return os.urandom(n).hex() | |
| @serve.tool | |
| def gen_key(n_per_part: int = 4, n_parts: int = 8) -> str: | |
| """Generate a random alphanumeric key in the format `XXXX-XXXX-...-XXXX`. | |
| Each part consists of `n_per_part` uppercase ASCII letters (A-Z) generated | |
| from cryptographically secure random bytes. The parts are joined with hyphens. | |
| Args: | |
| n_per_part: Number of characters per part (default 4). | |
| n_parts: Number of parts separated by hyphens (default 8). | |
| Returns: | |
| A string like `"ABCD-EFGH-IJKL-MNOP"`.""" | |
| result = [] | |
| for _ in range(n_parts): | |
| random_bytes = os.urandom(n_per_part) | |
| chars = [] | |
| for byte in random_bytes: | |
| chars.append(chr((byte & 0x1F) % 26 + 65)) | |
| result.append(''.join(chars)) | |
| return '-'.join(result) | |
| @serve.tool | |
| def kv_cache_size_bytes( | |
| n_head_kv: int, | |
| n_head_dim: int, | |
| n_layer: int, | |
| n_ctx: int, | |
| bits_per_key: int, | |
| bits_per_value: int | |
| ) -> int: | |
| """Calculate the total size (in bytes) of the KV cache for a transformer model. | |
| The KV cache stores the key and value tensors for each layer, head, token, and | |
| dimension, quantised to the specified bit widths. | |
| Args: | |
| n_head_kv: Number of key/value heads. | |
| n_head_dim: Dimension (size) of each head. | |
| n_layer: Number of transformer layers. | |
| n_ctx: Maximum context length (number of tokens). | |
| bits_per_key: Number of bits used to store each key element. | |
| bits_per_value: Number of bits used to store each value element. | |
| Returns: | |
| Total KV cache size in bytes. | |
| Formula: | |
| bytes = n_head_kv * n_head_dim * n_layer * n_ctx * (bits_per_key + bits_per_value) / 8""" | |
| return int(n_head_kv * n_head_dim * n_layer * n_ctx * (bits_per_key + bits_per_value) / 8) | |
| @serve.tool | |
| def is_close(a: int | float, b: int | float, factor: float = 0.995) -> bool: | |
| """Return whether `a` and `b` are close to one another (symmetric). | |
| `factor` should be a float in the range [0.0, 1.0] (inclusive).""" | |
| if not (0.0 <= factor <= 1.0): | |
| raise ValueError(f"factor must be in range [0, 1] (got {factor})") | |
| return factor <= a/b <= 2 - factor | |
| @serve.tool | |
| def seconds_from_now(seconds: int): | |
| """Return a `datetime` object offset from the current time by the given number of seconds.""" | |
| return datetime.datetime.now() + datetime.timedelta(seconds=seconds) | |
| # ------------------------------------------------------------------------------------------------ | |
| # | |
| # Web search, fetch, etc. | |
| # | |
| # ------------------------------------------------------------------------------------------------ | |
| @dataclass | |
| class SearchConfig: | |
| """Settings for the web-search backend.""" | |
| provider: str = "duckduckgo" | |
| user_agent: str = _LEGACY_UA | |
| region: str = "us-en" | |
| max_results: int = 25 | |
| timeout_seconds: int = 20 | |
| @dataclass | |
| class FetchConfig: | |
| """Settings for the page-fetch tool.""" | |
| browser_profile: str = "chrome136" | |
| max_content_bytes: int = 16384 | |
| timeout_seconds: int = 20 | |
| # | |
| # Exceptions | |
| # | |
| class SearchError(Exception): | |
| """A caller-facing problem with a search request (empty query, rate-limit, etc.).""" | |
| class FetchError(Exception): | |
| """A caller-facing problem with a page-fetch request (bad URL, unreachable, etc.).""" | |
| # | |
| # Search | |
| # | |
| def _unwrap_ddg_url(href: str) -> str: | |
| """Extract the real destination from a DuckDuckGo redirect URL. | |
| DDG wraps every result href in:: | |
| //duckduckgo.com/l/?uddg=<encoded>&rut=... | |
| Pull out and decode the ``uddg`` parameter. Also handles bare | |
| protocol-relative URLs and already-absolute URLs.""" | |
| if not href: | |
| return "" | |
| # DDG redirect: //duckduckgo.com/l/?uddg=https%3A%2F%2F...&rut=... | |
| m = re.search(r"[?&]uddg=([^&]+)", href) | |
| if m: | |
| return unquote(m.group(1)) | |
| # protocol-relative: //example.com/path | |
| if href.startswith("//"): | |
| return "https:" + href | |
| # already a normal URL | |
| return href | |
| def _parse_ddg_results(html: str, count: int) -> list[dict]: | |
| """Parse DuckDuckGo's server-rendered HTML into a list of result dicts. | |
| Each result dict has keys: *rank*, *title*, *url*, *displayUrl*, *snippet*. | |
| Sponsored (``result--ad``) entries are skipped.""" | |
| soup = BeautifulSoup(html, "lxml") | |
| results: list[dict] = [] | |
| for body in soup.find_all("div", class_="result__body"): | |
| if len(results) >= count: | |
| break | |
| # skip sponsored blocks | |
| container = body.parent | |
| if container and _has_class(container, "result--ad"): | |
| continue | |
| # title anchor | |
| anchor = body.find("a", class_="result__a") | |
| if not anchor: | |
| continue | |
| title = anchor.get_text(strip=True) | |
| url = _unwrap_ddg_url(anchor.get("href", "")) | |
| if not title or not url: | |
| continue | |
| # snippet | |
| snippet_el = body.find("a", class_="result__snippet") | |
| snippet = snippet_el.get_text(strip=True) if snippet_el else "" | |
| # display URL | |
| url_el = body.find("a", class_="result__url") | |
| display_url = url_el.get_text(strip=True) if url_el else None | |
| if display_url and not display_url.strip(): | |
| display_url = None | |
| results.append({ | |
| "rank": len(results) + 1, | |
| "title": title, | |
| "url": url, | |
| "displayUrl": display_url, | |
| "snippet": snippet, | |
| }) | |
| return results | |
| def _has_class(tag, class_name: str) -> bool: | |
| """Check whether a BeautifulSoup tag has *class_name* among its classes.""" | |
| classes = tag.get("class") | |
| return bool(classes) and class_name in classes | |
| def search_web(query: str, count: int = 10, config: Optional[SearchConfig] = None) -> str: | |
| """Search the web via DuckDuckGo's no-JavaScript HTML endpoint. | |
| Returns a JSON string. On success the top-level keys are: | |
| success, provider, query, count, results | |
| where *results* is a list of dicts with keys *rank*, *title*, *url*, | |
| *displayUrl*, *snippet*. | |
| On error the top-level keys are: | |
| success (``false``), error (description string) | |
| Args: | |
| query: The search query. | |
| count: Max results to return (clamped to 1-25, default 10). | |
| config: Optional overrides for user-agent, region, timeout, etc.""" | |
| if config is None: | |
| config = SearchConfig() | |
| query = query.strip() | |
| if not query: | |
| return {"success": False, "error": "search query must not be empty"} | |
| count = max(1, min(count, config.max_results)) | |
| url = f"{_DDG_ENDPOINT}?q={quote(query)}&kl={quote(config.region)}" | |
| headers = {"User-Agent": config.user_agent} | |
| try: | |
| resp = _requests.get(url, headers=headers, timeout=config.timeout_seconds) | |
| resp.raise_for_status() | |
| html = resp.text | |
| except Exception as exc: | |
| return {"success": False, "error": f"failed to fetch search results: {exc}"} | |
| # DDG rate-limit detection | |
| lower = html.lower() | |
| if "anomaly" in lower or "if this error persists" in lower: | |
| return { | |
| "success": False, | |
| "error": "DuckDuckGo temporarily rate-limited this request; wait a moment and retry", | |
| } | |
| try: | |
| results = _parse_ddg_results(html, count) | |
| except Exception as exc: | |
| return {"success": False, "error": f"Failed to parse search results: {exc}"} | |
| return { | |
| "success": True, | |
| "provider": "duckduckgo", | |
| "query": query, | |
| "count": len(results), | |
| "results": results, | |
| } | |
| def _strip_html(html: str) -> str: | |
| """Last-resort text extraction: strip non-content nodes, keep inner text. | |
| Used when trafilatura (Readability) fails to find a coherent article.""" | |
| soup = BeautifulSoup(html, "lxml") | |
| for tag in soup( | |
| ("script", "style", "noscript", "nav", "header", "footer", "svg", "form") | |
| ): | |
| tag.decompose() | |
| text = soup.get_text(separator=" ", strip=True) | |
| text = re.sub(r"\n[ \t\f\v]*(\n[ \t\f\v]*)+", "\n\n", text) | |
| text = re.sub(r"[ \t\f\v]+", " ", text) | |
| return text.strip() | |
| def _fetch_url(url: str, config: FetchConfig) -> str: | |
| """Fetch `url` with browser impersonation, falling back to plain requests.""" | |
| if HAS_CURL_CFFI: | |
| try: | |
| resp = _curl_requests.get( | |
| url, | |
| impersonate=config.browser_profile, | |
| timeout=config.timeout_seconds, | |
| allow_redirects=True, | |
| ) | |
| resp.raise_for_status() | |
| return resp.text | |
| except Exception as exc: | |
| _log.warning("curl_cffi request failed (%s); falling back to stdlib", exc) | |
| # fallback with a modern browser UA | |
| headers = { | |
| "User-Agent": ( | |
| "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " | |
| "Chrome/136.0.0.0 Safari/537.36" | |
| ), | |
| "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", | |
| "Accept-Language": "en-US,en;q=0.5", | |
| } | |
| resp = _requests.get( | |
| url, headers=headers, timeout=config.timeout_seconds, allow_redirects=True | |
| ) | |
| resp.raise_for_status() | |
| return resp.text | |
| def fetch_page( | |
| url: str, max_bytes: Optional[int] = None, config: Optional[FetchConfig] = None | |
| ) -> dict[str, object]: | |
| """Fetch a web page and return its main readable text with metadata. | |
| Uses ``curl_cffi`` for browser impersonation to bypass bot detection, | |
| then extracts the main article content via Readability (trafilatura), | |
| falling back to a plain HTML strip if extraction fails. | |
| Returns a JSON string. On success the top-level keys are: | |
| success, url, title, author, siteName, published, language, | |
| minutesToRead, readable, length, truncated, text | |
| On error the top-level keys are: | |
| success (``false``), error (description string) | |
| Args: | |
| url: The page URL to fetch (http or https). | |
| max_bytes: Max bytes of body text to return (default: 16_384). | |
| Longer content is truncated with a ``[… truncated]`` marker. | |
| config: Optional overrides for browser profile, timeout, etc.""" | |
| if config is None: | |
| config = FetchConfig() | |
| cap = max_bytes if (max_bytes is not None and max_bytes > 0) else config.max_content_bytes | |
| # validate URL | |
| url = url.strip() | |
| if not url: | |
| return {"success": False, "error": "a page URL is required"} | |
| if not (url.startswith("http://") or url.startswith("https://")): | |
| return {"success": False, "error": f"{repr(url)} is not a valid HTTP(S) URL"} | |
| # fetch | |
| try: | |
| html = _fetch_url(url, config) | |
| except Exception as exc: | |
| return {"success": False, "error": f"failed to fetch {repr(url)}: {exc}"} | |
| # extract readable content | |
| title = author = site_name = language = published = None | |
| minutes_to_read = None | |
| readable = False | |
| text: Optional[str] = None | |
| if HAS_TRAFILATURA: | |
| print("has trafilatura") | |
| try: | |
| result = _trafilatura.bare_extraction( | |
| html, | |
| with_metadata=True, | |
| include_comments=False, | |
| include_tables=True, | |
| include_images=False, | |
| include_formatting=False, | |
| no_fallback=False, | |
| ) | |
| if result: | |
| # support both dict-like and attribute access (varies by trafilatura version) | |
| def _get(obj, key): | |
| return obj[key] if isinstance(obj, dict) else getattr(obj, key, None) | |
| text = _get(result, "text") | |
| title = _get(result, "title") | |
| author = _get(result, "author") | |
| site_name = _get(result, "site_name") or _get(result, "source") | |
| published = _get(result, "date") | |
| language = _get(result, "language") | |
| if text and text.strip(): | |
| readable = True | |
| word_count = len(text.split()) | |
| minutes_to_read = max(1, word_count // 200) | |
| except Exception as exc: | |
| _log.warning("trafilatura extraction failed (%s)", exc) | |
| else: | |
| print("no trafilatura") | |
| # fallback to text extraction | |
| if not text or not text.strip(): | |
| text = _strip_html(html) | |
| if not text.strip(): | |
| return { | |
| "success": False, "error": "no readable content could be extracted from the page" | |
| } | |
| # truncate if needed | |
| truncated = False | |
| encoded = text.encode("utf-8") | |
| if len(encoded) > cap: | |
| encoded = encoded[:cap] | |
| text = encoded.decode("utf-8", errors="ignore").rstrip() + " [... truncated ...]" | |
| truncated = True | |
| return { | |
| "success": True, | |
| "url": url, | |
| "title": title, | |
| "author": author, | |
| "siteName": site_name, | |
| "published": published, | |
| "language": language, | |
| "minutesToRead": minutes_to_read, | |
| "readable": readable, | |
| "length": len(text.encode("utf-8")), | |
| "truncated": truncated, | |
| "text": text, | |
| } | |
| @serve.tool | |
| def web_search(query: str, count: int = 10) -> dict: | |
| """Search the web via DuckDuckGo. Returns JSON with titles, URLs, snippets.""" | |
| return search_web(query, count) | |
| @serve.tool | |
| def web_fetch(url: str, max_bytes: int = 16384) -> dict: | |
| """Fetch a web page and extract its main readable content. Returns JSON.""" | |
| return fetch_page(url, max_bytes) | |
| if __name__ == "__main__": | |
| serve.run("http") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment