Skip to content

Commit

Permalink
Packagifying functionsmith
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 714443499
  • Loading branch information
The Google Earth Engine Community Authors authored and copybara-github committed Jan 14, 2025
1 parent f5d8bb3 commit a98d5d9
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 46 deletions.
17 changes: 13 additions & 4 deletions experimental/functionsmith/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,21 @@ limitations under the License.

## Overview

Functionsmith is a general-purpose problem-solving agent.
Functionsmith is a general-purpose problem-solving agent using dynamic
function calling.

USING THIS AGENT IS UNSAFE. It directly runs LLM-produced code, and thus
should only be used for demonstration purposes.

This agent uses free-form function calling, which means that instead of
To run with the default task investigating a CSV file with airport data,
install the package, then run:
```
export GOOGLE_API_KEY=my-api-key
wget https://raw.githubusercontent.com/davidmegginson/ourairports-data/refs/heads/main/airports.csv
functionsmith_cli
```

This agent uses dynamic function calling, which means that instead of
relying on a fixed set of tools predefined in the agent
[in normal LLM function calling](https://ai.google.dev/gemini-api/docs/function-calling),
we let the agent itself write with all the functions it needs.
Expand All @@ -43,8 +52,8 @@ locally.

The functions are not saved permanently, though this feature can be added.

# TODO(simonf): add a notebook version of the general-purpose agent,
# as well as an Earth Engine-specific notebook agent.
TODO(simonf): add a notebook version of the general-purpose agent,
as well as an Earth Engine-specific notebook agent.

## Attribution

Expand Down
49 changes: 49 additions & 0 deletions experimental/functionsmith/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
[project]
name = "functionsmith"
version = "0.1.0"
description = "Functionsmith is a general-purpose problem-solving agent using dynamic function calling"
readme = "README.md"
requires-python = ">=3.9"
keywords = [
"ai",
"agent",
"llm"
]
license = {text = "Apache-2.0"}
authors = [
{name = "Simon Ilyushchenko", email = "simonf@google.com"},
]
classifiers = [
# Get strings from
# http://pypi.python.org/pypi?%3Aaction=list_classifiers
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Typing :: Typed",
]
dependencies = [
"anthropic",
"docstring_parser",
"google-genai",
"openai",
]

[build-system]
requires = ['setuptools', 'wheel']

[project.entry-points."console_scripts"]
functionsmith_cli = "agent:main"

[project.urls]
Homepage = "https://github.com/google/earthengine-community/tree/master/experimental/functionsmith"
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
USING THIS AGENT IS UNSAFE. It directly runs LLM-produced code, and thus
should only be used for demonstration purposes.
To get started, get a data file:
To get started, set one of these environment variables to your API key:
GOOGLE_API_KEY, ANTHROPIC_API_KEY, or OPENAI_API_KEY.
To execute the sample task analyzing airports, get the airports.csv file:
```
wget https://raw.githubusercontent.com/davidmegginson/ourairports-data/refs/heads/main/airports.csv
```
Expand All @@ -17,9 +20,6 @@
See README.md for more information.
To execute the sample task analyzing airports, get the airtports.csv file from
https://raw.githubusercontent.com/davidmegginson/ourairports-data/refs/heads/main/airports.csv
The agent will ask the LLM to find "something interesting" about the data
given its schema. Then the LLM will probably create one or two sets of
low-level function with tests, then actually analyze the files, then stop
Expand Down Expand Up @@ -195,45 +195,51 @@ def task_done(agent_message: str) -> None:

tools = {}

question = task

while True:
print(STARS)
all_tools = copy.deepcopy(tools)
all_tools.update(syscalls)
question_with_tools = (
question
+ 'The following functions are available:\n'
+ '\n'.join([str(x) for x in all_tools.values()])
)
response = agent.chat(question_with_tools)
print(response)
def main():
question = task

parsed_response = parser.extract_functions(response)
if not parsed_response.code and not parsed_response.functions:
if parsed_response.error:
question = parsed_response.error
continue
question = input('> ')
continue

tools.update(parsed_response.functions)

if parsed_response.code:
# Concatenate all known source code together.
# Functions that were defined in the most recent response will be repeated,
# which is okay
code_with_tools = (
'\n'.join([x.code for x in tools.values()])
+ '\n'
+ parsed_response.code
while True:
print(STARS)
all_tools = copy.deepcopy(tools)
all_tools.update(syscalls)
question_with_tools = (
question
+ 'The following functions are available:\n'
+ '\n'.join([str(x) for x in all_tools.values()])
)
response = agent.chat(question_with_tools)
print(response)

parsed_response = parser.extract_functions(response)
if not parsed_response.code and not parsed_response.functions:
if parsed_response.error:
question = parsed_response.error
continue
question = input('> ')
continue

print(STARS)
input('HIT ENTER TO RUN THIS CODE')
print(STARS)
question = code_executor.run_code(code_with_tools, {'task_done': task_done})
else:
# The response had functions but no code. The agent wanted to define them.
# We tell it to go on (that is, to keep writing code).
question = 'go on'
tools.update(parsed_response.functions)

if parsed_response.code:
# Concatenate all known source code together.
# Functions that were defined in the most recent response will be repeated,
# which is okay
code_with_tools = (
'\n'.join([x.code for x in tools.values()])
+ '\n'
+ parsed_response.code
)

print(STARS)
input('HIT ENTER TO RUN THIS CODE')
print(STARS)
question = code_executor.run_code(code_with_tools, {'task_done': task_done})
else:
# The response had functions but no code. The agent wanted to define them.
# We tell it to go on (that is, to keep writing code).
question = 'go on'


if __name__ == '__main__':
main()
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit a98d5d9

Please sign in to comment.