From 8c507e58a416adf60b996617c8ef0d35b51bf3bb Mon Sep 17 00:00:00 2001 From: Kerem Turgutlu Date: Tue, 26 May 2026 12:42:20 +0300 Subject: [PATCH] fixes #88 --- 01_funccall.ipynb | 153 +++++++++++++++++++++++++++++++++++++++++++- toolslm/_modidx.py | 6 +- toolslm/funccall.py | 24 ++++++- 3 files changed, 180 insertions(+), 3 deletions(-) diff --git a/01_funccall.ipynb b/01_funccall.ipynb index 229edc1..bd931ac 100644 --- a/01_funccall.ipynb +++ b/01_funccall.ipynb @@ -2782,6 +2782,157 @@ "test_eq(_py_nm(''), '_')" ] }, + { + "cell_type": "markdown", + "id": "88efff10", + "metadata": {}, + "source": [ + "## Arguments Stripping" + ] + }, + { + "cell_type": "markdown", + "id": "4fc843e3", + "metadata": {}, + "source": [ + "Some models like Codex tend to also pass the default argument values during the tool calls even though they are instructed against it. So we introduce some helpers that strip the defaults to reduce the noise for the rest of the conversation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cd26f9a", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "def _tool_defaults(tool_schemas, pname):\n", + " \"Get default arg values from tool schemas\"\n", + " res = {}\n", + " for t in tool_schemas or []:\n", + " sch = t.get('function') or t\n", + " fn = nested_idx(sch, 'name')\n", + " props = nested_idx(sch, pname, 'properties') or {}\n", + " ds = {k:v['default'] for k,v in props.items() if isinstance(v,dict) and 'default' in v}\n", + " if fn and ds: res[fn] = ds\n", + " return res\n", + "\n", + "def _strip_arg_defaults(args, defaults):\n", + " \"Remove args matching schema defaults\"\n", + " if not args or not defaults: return args\n", + " return {k:v for k,v in args.items() if k not in defaults or v!=defaults[k]}\n", + "\n", + "def strip_tool_arg_defaults(tcs, tool_schemas, pname='parameters'):\n", + " \"Strip default args from tool call mappings\"\n", + " defaults = _tool_defaults(tool_schemas, pname)\n", + " return [_strip_arg_defaults(o.get('arguments'), defaults.get(o.get('name'))) for o in tcs]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fcfe2d1c", + "metadata": {}, + "outputs": [], + "source": [ + "def defaults_tool(a:int, b:int=0, flag:bool=False, name:str='x', ratio:float=1.0) -> str:\n", + " \"Test defaults of different primitive types\"\n", + " return f'{a=} {b=} {flag=} {name=} {ratio=}'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1f538fd8", + "metadata": {}, + "outputs": [], + "source": [ + "tcs = [dict(name='defaults_tool', arguments=dict(a=1234, b=0, flag=False, name='x', ratio=1.0), server=False)]\n", + "test_eq(strip_tool_arg_defaults(tcs, [get_schema(defaults_tool)], 'input_schema'), [{'a':1234}])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c94cb9ef", + "metadata": {}, + "outputs": [], + "source": [ + "def lite_mk_func(f): return {'type':'function', 'function':get_schema(f, pname='parameters')}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66c75b43", + "metadata": {}, + "outputs": [], + "source": [ + "test_eq(strip_tool_arg_defaults(tcs, [lite_mk_func(defaults_tool)]), [{'a':1234}])" + ] + }, + { + "cell_type": "markdown", + "id": "c6c07036", + "metadata": {}, + "source": [ + "No defaults" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bd6bd237", + "metadata": {}, + "outputs": [], + "source": [ + "def simple_add(a:int,b:int):\n", + " 'add'\n", + " return a + b" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c61170b0", + "metadata": {}, + "outputs": [], + "source": [ + "tcs = [dict(name='defaults_tool', arguments=dict(a=1234, b=0))]\n", + "test_eq(strip_tool_arg_defaults(tcs, [lite_mk_func(simple_add)]), [{'a':1234, 'b':0}])" + ] + }, + { + "cell_type": "markdown", + "id": "b9433560", + "metadata": {}, + "source": [ + "No args" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f1fbbb0e", + "metadata": {}, + "outputs": [], + "source": [ + "def hello():\n", + " 'hello'\n", + " return 'hello'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38d749ba", + "metadata": {}, + "outputs": [], + "source": [ + "tcs = [dict(name='hello', arguments=dict())]\n", + "test_eq(strip_tool_arg_defaults(tcs, [lite_mk_func(hello)]), [{}])" + ] + }, { "cell_type": "markdown", "id": "94ec4289", @@ -2809,7 +2960,7 @@ "default_code": false, "mode": "concise", "use_fence": false, - "use_thinking": false, + "use_thinking": true, "use_tools": true, "ver": 2 } diff --git a/toolslm/_modidx.py b/toolslm/_modidx.py index ae332cc..541a8e6 100644 --- a/toolslm/_modidx.py +++ b/toolslm/_modidx.py @@ -25,6 +25,8 @@ 'toolslm.funccall._process_property': ('funccall.html#_process_property', 'toolslm/funccall.py'), 'toolslm.funccall._py_nm': ('funccall.html#_py_nm', 'toolslm/funccall.py'), 'toolslm.funccall._run': ('funccall.html#_run', 'toolslm/funccall.py'), + 'toolslm.funccall._strip_arg_defaults': ('funccall.html#_strip_arg_defaults', 'toolslm/funccall.py'), + 'toolslm.funccall._tool_defaults': ('funccall.html#_tool_defaults', 'toolslm/funccall.py'), 'toolslm.funccall._type_str': ('funccall.html#_type_str', 'toolslm/funccall.py'), 'toolslm.funccall.call_func': ('funccall.html#call_func', 'toolslm/funccall.py'), 'toolslm.funccall.call_func_async': ('funccall.html#call_func_async', 'toolslm/funccall.py'), @@ -36,7 +38,9 @@ 'toolslm.funccall.mk_param': ('funccall.html#mk_param', 'toolslm/funccall.py'), 'toolslm.funccall.mk_tool': ('funccall.html#mk_tool', 'toolslm/funccall.py'), 'toolslm.funccall.resolve_nm': ('funccall.html#resolve_nm', 'toolslm/funccall.py'), - 'toolslm.funccall.schema2sig': ('funccall.html#schema2sig', 'toolslm/funccall.py')}, + 'toolslm.funccall.schema2sig': ('funccall.html#schema2sig', 'toolslm/funccall.py'), + 'toolslm.funccall.strip_tool_arg_defaults': ( 'funccall.html#strip_tool_arg_defaults', + 'toolslm/funccall.py')}, 'toolslm.inspecttools': { 'toolslm.inspecttools.SymbolNotFound': ( 'inspecttools.html#symbolnotfound', 'toolslm/inspecttools.py'), 'toolslm.inspecttools.SymbolNotFound.__repr__': ( 'inspecttools.html#symbolnotfound.__repr__', diff --git a/toolslm/funccall.py b/toolslm/funccall.py index da35845..37f059d 100644 --- a/toolslm/funccall.py +++ b/toolslm/funccall.py @@ -4,7 +4,7 @@ # %% auto #0 __all__ = ['empty', 'custom_types', 'type_map', 'get_schema', 'minipy', 'mk_ns', 'coerce_inputs', 'resolve_nm', 'get_schema_nm', - 'call_func', 'call_func_async', 'mk_param', 'schema2sig', 'mk_tool'] + 'call_func', 'call_func_async', 'mk_param', 'schema2sig', 'mk_tool', 'strip_tool_arg_defaults'] # %% ../01_funccall.ipynb #e5ad6b86 import inspect, ast, keyword @@ -315,3 +315,25 @@ def fn(*args, **kwargs): fn.__name__ = fn.__qualname__ = tool.name fn.__annotations__ = {k: p.annotation for k, p in sig.parameters.items()} return fn + +# %% ../01_funccall.ipynb #2cd26f9a +def _tool_defaults(tool_schemas, pname): + "Get default arg values from tool schemas" + res = {} + for t in tool_schemas or []: + sch = t.get('function') or t + fn = nested_idx(sch, 'name') + props = nested_idx(sch, pname, 'properties') or {} + ds = {k:v['default'] for k,v in props.items() if isinstance(v,dict) and 'default' in v} + if fn and ds: res[fn] = ds + return res + +def _strip_arg_defaults(args, defaults): + "Remove args matching schema defaults" + if not args or not defaults: return args + return {k:v for k,v in args.items() if k not in defaults or v!=defaults[k]} + +def strip_tool_arg_defaults(tcs, tool_schemas, pname='parameters'): + "Strip default args from tool call mappings" + defaults = _tool_defaults(tool_schemas, pname) + return [_strip_arg_defaults(o.get('arguments'), defaults.get(o.get('name'))) for o in tcs]