Skip to content

Commit

Permalink
Robch/2310 oct22 chat proto func (#64)
Browse files Browse the repository at this point in the history
* initial ability to call chat protocol function

* updated function_call script to work with copilots from aistudio-copilot-sample repo oct-refresh branch

* refactor environment helpers

* refactor; set environment before calling chat functions

* move file

* update help

* move file again

* added --question support to `ai chat`
  • Loading branch information
robch authored Oct 22, 2023
1 parent 3cb8ee1 commit 5d965dd
Show file tree
Hide file tree
Showing 7 changed files with 320 additions and 182 deletions.
35 changes: 35 additions & 0 deletions ideas/app_func.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import os
from typing import Any, List

def chat_completion(question: str) -> List[str]:
answers = []

# Implement a switch statement for various questions
if question == "what is your name?":
answers.append("My name is ChatGPT.")
elif question == "what is the capital of France?":
answers.append("The capital of France is Paris.")
elif question == "what is 2 + 2?":
answers.append("2 + 2 is 4.")
elif question == "tell me a joke":
answers.append("Why did the chicken cross the road? To get to the other side!")
elif question == "show environment variables":
answers.append("Here are the environment variables:\n\n" + "\n".join([f" {k}: {v}" for k, v in os.environ.items()]))
else:
answers.append("I don't know the answer to that question.")
return answers

return answers

async def async_chat_completion(messages: list[dict] = None, stream: bool = False,
session_state: Any = None, context: dict[str, Any] = {}):

if (messages is None):
return chat_completion(question)

print(messages)

# get search documents for the last user message in the conversation
question = messages[-1]["content"]

return chat_completion(question)
13 changes: 10 additions & 3 deletions src/ai/.x/help/chat
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ USAGE: ai chat [...]
--system PROMPT (see: ai help chat system prompt)
--user MESSAGE (see: ai help chat user message)

CHAT WITH FUNCTION
--function MODULE:FUNCTION (see: ai help chat function)

CHAT WITH DATA (see: ai help chat with data)
--index-name INDEX (see: ai help index name)
--search-endpoint ENDPOINT (see: ai help search endpoint)
Expand Down Expand Up @@ -42,9 +45,13 @@ EXAMPLES

`EXAMPLE 4`: Chat w/ data using Azure Search vector index

ai config @search. --set MyIndex
ai search index create --name @myindex --files *.txt --interactive
ai chat --index-name @myindex --interactive
ai search index update --name contoso_product_index --files *.md
ai chat --index-name contoso_product_index --interactive
ai config --clear search.index.name

`EXAMPLE 5`: Chat w/ function a specific python function

ai chat --function copilot_aisdk:chat_completion --interactive

SEE ALSO

Expand Down
72 changes: 47 additions & 25 deletions src/ai/.x/help/include.python.script.function_call.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,51 @@
# call_function.py

import asyncio
import argparse
import importlib
import inspect
import json
import os
import sys
from typing import Generator
from typing import Any, List, Dict, Generator

def ensure_and_strip_module_path(module_path) -> str:
async def ensure_and_strip_module_path(module_path) -> str:
module_path = os.path.join(os.getcwd(), module_path)
module_name = os.path.basename(module_path)

print("current working directory: " + os.getcwd())

if os.path.exists(module_path + ".py"):
module_dirname = os.path.dirname(module_path)
if module_dirname not in sys.path:
sys.path.append(module_dirname)
if os.getcwd() not in sys.path:
sys.path.append(os.getcwd())
return module_name

raise ModuleNotFoundError("Module not found: " + module_path)
def call_function(module_path: str, function_name: str, json_params: str):

async def call_async_function(module_path: str, function_name: str, json_params: str):
try:
module_name = ensure_and_strip_module_path(module_path)
module_name = await ensure_and_strip_module_path(module_path)

# Import the module
target_module = importlib.import_module(module_name)

# Use inspect to find the function by name
target_function = getattr(target_module, function_name)

# Check if the target_function is a coroutine function
if asyncio.iscoroutinefunction(target_function):
# Parse the JSON parameter string to a dictionary
params = json.loads(json_params)

# Call the asynchronous function
result = await target_function(**params)
return result

# Check if the target_function is callable
if callable(target_function):
elif callable(target_function):
# Parse the JSON parameter string to a dictionary
params = json.loads(json_params)

Expand All @@ -44,9 +60,9 @@ def call_function(module_path: str, function_name: str, json_params: str):
return result

else:
return "Function not found or not callable."
# handle TypeError
return "Function not found, not an asynchronous function, or not callable."

# Handle exceptions
except ModuleNotFoundError:
print("Module not found: " + module_path)
raise
Expand All @@ -61,50 +77,56 @@ def call_function(module_path: str, function_name: str, json_params: str):
raise

def ensure_args() -> list:
parser = argparse.ArgumentParser(description="Call a function in a module with specified parameters.")
parser = argparse.ArgumentParser(description="Call a function (async or not) in a module with specified parameters.")
parser.add_argument("--function", required=True, help="Module and function name in the format MODULE:FUNCTION.")
parser.add_argument("--parameters", default="{}", help="JSON string containing parameters.")
args = parser.parse_args()

return args.function, args.parameters

def main():
async def main():
function, json_params = ensure_args()
module_function_parts = function.rsplit(":", 1)

if len(module_function_parts) != 2:
print("Invalid argument format. Please use MODULE:FUNCTION.")
sys.exit(1)

module_name = module_function_parts[0]
function_name = module_function_parts[1]

result = call_function(module_name, function_name, json_params)
result = await call_async_function(module_name, function_name, json_params)

if result is not None:
# if it's a string...
if isinstance(result, str):
print("---it's a string---")
for word in result.split():
print(word)

# if it's a list of strings
print(result)

elif isinstance(result, list) and all(isinstance(item, str) for item in result):
print("---it's a list---")
for item in result:
for word in item.split():
print(word)
print(item)

# if it's a generator
elif issubclass(type(result), Generator) :
print("---it's a generator---")
for item in result:
for word in item.split():
print(word)
print(item)

# if the "openai.openai_object.OpenAIObject"
elif (type(result).__name__ == "OpenAIObject"):
print("---it's an OpenAIObject---")
print(result.choices[0].message.content)

# if it's a dictionary that has a "choices" key
elif (isinstance(result, dict) and "choices" in result):
print("---it's a dictionary with a 'choices' key---")
print(result["choices"][0]["message"]["content"])

else:
print("---it's something else---")
print(type(result))
print(result)

if __name__ == "__main__":
main()
asyncio.run(main()) # Use asyncio.run() to run the asynchronous main function
Loading

0 comments on commit 5d965dd

Please sign in to comment.