Allow in question -s for just sourcing file and -S to source file with ``` encapsulation and port print arguments -q/-a/-S from main to restructuring. #6
@ -14,11 +14,11 @@ def create_ai(args: argparse.Namespace, config: Config) -> AI:
|
|||||||
Creates an AI subclass instance from the given arguments
|
Creates an AI subclass instance from the given arguments
|
||||||
and configuration file.
|
and configuration file.
|
||||||
"""
|
"""
|
||||||
if args.ai:
|
if args.AI:
|
||||||
try:
|
try:
|
||||||
ai_conf = config.ais[args.ai]
|
ai_conf = config.ais[args.AI]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise AIError(f"AI ID '{args.ai}' does not exist in this configuration")
|
raise AIError(f"AI ID '{args.AI}' does not exist in this configuration")
|
||||||
elif default_ai_ID in config.ais:
|
elif default_ai_ID in config.ais:
|
||||||
ai_conf = config.ais[default_ai_ID]
|
ai_conf = config.ais[default_ai_ID]
|
||||||
else:
|
else:
|
||||||
@ -34,4 +34,4 @@ def create_ai(args: argparse.Namespace, config: Config) -> AI:
|
|||||||
ai.config.temperature = args.temperature
|
ai.config.temperature = args.temperature
|
||||||
return ai
|
return ai
|
||||||
else:
|
else:
|
||||||
raise AIError(f"AI '{args.ai}' is not supported")
|
raise AIError(f"AI '{args.AI}' is not supported")
|
||||||
|
|||||||
@ -13,7 +13,15 @@ def print_cmd(args: argparse.Namespace, config: Config) -> None:
|
|||||||
try:
|
try:
|
||||||
message = Message.from_file(fname)
|
message = Message.from_file(fname)
|
||||||
if message:
|
if message:
|
||||||
print(message.to_str(source_code_only=args.source_code_only))
|
if args.question:
|
||||||
|
print(message.question)
|
||||||
|
elif args.answer:
|
||||||
|
print(message.answer)
|
||||||
|
elif message.answer and args.only_source_code:
|
||||||
|
for code in message.answer.source_code():
|
||||||
|
print(code)
|
||||||
|
else:
|
||||||
|
print(message.to_str())
|
||||||
except MessageError:
|
except MessageError:
|
||||||
print(f"File is not a valid message: {args.file}")
|
print(f"File is not a valid message: {args.file}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
@ -13,27 +13,29 @@ def create_message(chat: ChatDB, args: argparse.Namespace) -> Message:
|
|||||||
Creates (and writes) a new message from the given arguments.
|
Creates (and writes) a new message from the given arguments.
|
||||||
"""
|
"""
|
||||||
question_parts = []
|
question_parts = []
|
||||||
question_list = args.question if args.question is not None else []
|
question_list = args.ask if args.ask is not None else []
|
||||||
source_list = args.source if args.source is not None else []
|
source_list = args.source if args.source is not None else []
|
||||||
|
code_list = args.source_code if args.source_code is not None else []
|
||||||
|
|
||||||
# FIXME: don't surround all sourced files with ```
|
for question, source, code in zip_longest(question_list, source_list, code_list, fillvalue=None):
|
||||||
# -> do it only if '--source-code-only' is True and no source code
|
if question is not None and len(question.strip()) > 0:
|
||||||
# could be extracted from that file
|
|
||||||
for question, source in zip_longest(question_list, source_list, fillvalue=None):
|
|
||||||
if question is not None and source is not None:
|
|
||||||
with open(source) as r:
|
|
||||||
question_parts.append(f"{question}\n\n```\n{r.read().strip()}\n```")
|
|
||||||
elif question is not None:
|
|
||||||
question_parts.append(question)
|
question_parts.append(question)
|
||||||
elif source is not None:
|
if source is not None and len(source) > 0:
|
||||||
with open(source) as r:
|
with open(source) as r:
|
||||||
question_parts.append(f"```\n{r.read().strip()}\n```")
|
content = r.read().strip()
|
||||||
|
if len(content) > 0:
|
||||||
|
question_parts.append(content)
|
||||||
|
if code is not None and len(code) > 0:
|
||||||
|
with open(code) as r:
|
||||||
|
content = r.read().strip()
|
||||||
|
if len(content) > 0:
|
||||||
|
question_parts.append(f"```\n{content}\n```")
|
||||||
|
|
||||||
full_question = '\n\n'.join(question_parts)
|
full_question = '\n\n'.join(question_parts)
|
||||||
|
|
||||||
message = Message(question=Question(full_question),
|
message = Message(question=Question(full_question),
|
||||||
tags=args.output_tags, # FIXME
|
tags=args.output_tags, # FIXME
|
||||||
ai=args.ai,
|
ai=args.AI,
|
||||||
model=args.model)
|
model=args.model)
|
||||||
chat.add_to_cache([message])
|
chat.add_to_cache([message])
|
||||||
return message
|
return message
|
||||||
@ -57,7 +59,7 @@ def question_cmd(args: argparse.Namespace, config: Config) -> None:
|
|||||||
response: AIResponse = ai.request(message,
|
response: AIResponse = ai.request(message,
|
||||||
chat,
|
chat,
|
||||||
args.num_answers, # FIXME
|
args.num_answers, # FIXME
|
||||||
args.otags) # FIXME
|
args.output_tags) # FIXME
|
||||||
assert response
|
assert response
|
||||||
# TODO:
|
# TODO:
|
||||||
# * add answer to the message above (and create
|
# * add answer to the message above (and create
|
||||||
|
|||||||
@ -67,9 +67,8 @@ def create_parser() -> argparse.ArgumentParser:
|
|||||||
question_group.add_argument('-p', '--process', nargs='*', help='Process existing questions')
|
question_group.add_argument('-p', '--process', nargs='*', help='Process existing questions')
|
||||||
question_cmd_parser.add_argument('-O', '--overwrite', help='Overwrite existing messages when repeating them',
|
question_cmd_parser.add_argument('-O', '--overwrite', help='Overwrite existing messages when repeating them',
|
||||||
action='store_true')
|
action='store_true')
|
||||||
question_cmd_parser.add_argument('-s', '--source', nargs='+', help='Source add content of a file to the query')
|
question_cmd_parser.add_argument('-s', '--source', nargs='+', help='Add content of a file to the query')
|
||||||
question_cmd_parser.add_argument('-S', '--source-code-only', help='Add pure source code to the chat history',
|
question_cmd_parser.add_argument('-S', '--source-code', nargs='+', help='Add source code file content to the chat history')
|
||||||
action='store_true')
|
|
||||||
|
|
||||||
# 'hist' command parser
|
# 'hist' command parser
|
||||||
hist_cmd_parser = cmdparser.add_parser('hist', parents=[tag_parser],
|
hist_cmd_parser = cmdparser.add_parser('hist', parents=[tag_parser],
|
||||||
@ -114,8 +113,10 @@ def create_parser() -> argparse.ArgumentParser:
|
|||||||
aliases=['p'])
|
aliases=['p'])
|
||||||
print_cmd_parser.set_defaults(func=print_cmd)
|
print_cmd_parser.set_defaults(func=print_cmd)
|
||||||
print_cmd_parser.add_argument('-f', '--file', help='File to print', required=True)
|
print_cmd_parser.add_argument('-f', '--file', help='File to print', required=True)
|
||||||
print_cmd_parser.add_argument('-S', '--source-code-only', help='Print source code only (from the answer, if available)',
|
print_cmd_modes = print_cmd_parser.add_mutually_exclusive_group()
|
||||||
action='store_true')
|
print_cmd_modes.add_argument('-q', '--question', help='Print only question', action='store_true')
|
||||||
|
print_cmd_modes.add_argument('-a', '--answer', help='Print only answer', action='store_true')
|
||||||
|
print_cmd_modes.add_argument('-S', '--only-source-code', help='Print only source code', action='store_true')
|
||||||
|
|
||||||
argcomplete.autocomplete(parser)
|
argcomplete.autocomplete(parser)
|
||||||
return parser
|
return parser
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from chatmastermind.ais.openai import OpenAI
|
|||||||
class TestCreateAI(unittest.TestCase):
|
class TestCreateAI(unittest.TestCase):
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
self.args = MagicMock(spec=argparse.Namespace)
|
self.args = MagicMock(spec=argparse.Namespace)
|
||||||
self.args.ai = 'default'
|
self.args.AI = 'default'
|
||||||
self.args.model = None
|
self.args.model = None
|
||||||
self.args.max_tokens = None
|
self.args.max_tokens = None
|
||||||
self.args.temperature = None
|
self.args.temperature = None
|
||||||
@ -18,19 +18,19 @@ class TestCreateAI(unittest.TestCase):
|
|||||||
def test_create_ai_from_args(self) -> None:
|
def test_create_ai_from_args(self) -> None:
|
||||||
# Create an AI with the default configuration
|
# Create an AI with the default configuration
|
||||||
config = Config()
|
config = Config()
|
||||||
self.args.ai = 'default'
|
self.args.AI = 'default'
|
||||||
ai = create_ai(self.args, config)
|
ai = create_ai(self.args, config)
|
||||||
self.assertIsInstance(ai, OpenAI)
|
self.assertIsInstance(ai, OpenAI)
|
||||||
|
|
||||||
def test_create_ai_from_default(self) -> None:
|
def test_create_ai_from_default(self) -> None:
|
||||||
self.args.ai = None
|
self.args.AI = None
|
||||||
# Create an AI with the default configuration
|
# Create an AI with the default configuration
|
||||||
config = Config()
|
config = Config()
|
||||||
ai = create_ai(self.args, config)
|
ai = create_ai(self.args, config)
|
||||||
self.assertIsInstance(ai, OpenAI)
|
self.assertIsInstance(ai, OpenAI)
|
||||||
|
|
||||||
def test_create_empty_ai_error(self) -> None:
|
def test_create_empty_ai_error(self) -> None:
|
||||||
self.args.ai = None
|
self.args.AI = None
|
||||||
# Create Config with empty AIs
|
# Create Config with empty AIs
|
||||||
config = Config()
|
config = Config()
|
||||||
config.ais = {}
|
config.ais = {}
|
||||||
@ -40,7 +40,7 @@ class TestCreateAI(unittest.TestCase):
|
|||||||
|
|
||||||
def test_create_unsupported_ai_error(self) -> None:
|
def test_create_unsupported_ai_error(self) -> None:
|
||||||
# Mock argparse.Namespace with ai='invalid_ai'
|
# Mock argparse.Namespace with ai='invalid_ai'
|
||||||
self.args.ai = 'invalid_ai'
|
self.args.AI = 'invalid_ai'
|
||||||
# Create default Config
|
# Create default Config
|
||||||
config = Config()
|
config = Config()
|
||||||
# Call create_ai function and assert that it raises AIError
|
# Call create_ai function and assert that it raises AIError
|
||||||
|
|||||||
@ -23,8 +23,8 @@ class TestMessageCreate(unittest.TestCase):
|
|||||||
# create arguments mock
|
# create arguments mock
|
||||||
self.args = MagicMock(spec=argparse.Namespace)
|
self.args = MagicMock(spec=argparse.Namespace)
|
||||||
self.args.source = None
|
self.args.source = None
|
||||||
self.args.source_code_only = False
|
self.args.source_code = None
|
||||||
self.args.ai = None
|
self.args.AI = None
|
||||||
self.args.model = None
|
self.args.model = None
|
||||||
self.args.output_tags = None
|
self.args.output_tags = None
|
||||||
# create some files for sourcing
|
# create some files for sourcing
|
||||||
@ -59,7 +59,7 @@ Language is called 'brainfart'."""
|
|||||||
return list(Path(tmp_dir.name).glob('*.[ty]*'))
|
return list(Path(tmp_dir.name).glob('*.[ty]*'))
|
||||||
|
|
||||||
def test_message_file_created(self) -> None:
|
def test_message_file_created(self) -> None:
|
||||||
self.args.question = ["What is this?"]
|
self.args.ask = ["What is this?"]
|
||||||
cache_dir_files = self.message_list(self.cache_path)
|
cache_dir_files = self.message_list(self.cache_path)
|
||||||
self.assertEqual(len(cache_dir_files), 0)
|
self.assertEqual(len(cache_dir_files), 0)
|
||||||
create_message(self.chat, self.args)
|
create_message(self.chat, self.args)
|
||||||
@ -70,14 +70,14 @@ Language is called 'brainfart'."""
|
|||||||
self.assertEqual(message.question, Question("What is this?")) # type: ignore [union-attr]
|
self.assertEqual(message.question, Question("What is this?")) # type: ignore [union-attr]
|
||||||
|
|
||||||
def test_single_question(self) -> None:
|
def test_single_question(self) -> None:
|
||||||
self.args.question = ["What is this?"]
|
self.args.ask = ["What is this?"]
|
||||||
message = create_message(self.chat, self.args)
|
message = create_message(self.chat, self.args)
|
||||||
self.assertIsInstance(message, Message)
|
self.assertIsInstance(message, Message)
|
||||||
self.assertEqual(message.question, Question("What is this?"))
|
self.assertEqual(message.question, Question("What is this?"))
|
||||||
self.assertEqual(len(message.question.source_code()), 0)
|
self.assertEqual(len(message.question.source_code()), 0)
|
||||||
|
|
||||||
def test_multipart_question(self) -> None:
|
def test_multipart_question(self) -> None:
|
||||||
self.args.question = ["What is this", "'bard' thing?", "Is it good?"]
|
self.args.ask = ["What is this", "'bard' thing?", "Is it good?"]
|
||||||
message = create_message(self.chat, self.args)
|
message = create_message(self.chat, self.args)
|
||||||
self.assertIsInstance(message, Message)
|
self.assertIsInstance(message, Message)
|
||||||
self.assertEqual(message.question, Question("""What is this
|
self.assertEqual(message.question, Question("""What is this
|
||||||
@ -87,25 +87,39 @@ Language is called 'brainfart'."""
|
|||||||
Is it good?"""))
|
Is it good?"""))
|
||||||
|
|
||||||
def test_single_question_with_text_only_source(self) -> None:
|
def test_single_question_with_text_only_source(self) -> None:
|
||||||
self.args.question = ["What is this?"]
|
self.args.ask = ["What is this?"]
|
||||||
self.args.source = [f"{self.source_file1.name}"]
|
self.args.source = [f"{self.source_file1.name}"]
|
||||||
message = create_message(self.chat, self.args)
|
message = create_message(self.chat, self.args)
|
||||||
self.assertIsInstance(message, Message)
|
self.assertIsInstance(message, Message)
|
||||||
# source file contains no source code
|
# source file contains no source code
|
||||||
# -> don't expect any in the question
|
# -> don't expect any in the question
|
||||||
self.assertEqual(len(message.question.source_code()), 0)
|
self.assertEqual(len(message.question.source_code()), 0)
|
||||||
self.assertEqual(message.question, Question("""What is this?
|
self.assertEqual(message.question, Question(f"""What is this?
|
||||||
|
|
||||||
{self.source_file1_content}"""))
|
{self.source_file1_content}"""))
|
||||||
|
|
||||||
def test_single_question_with_embedded_source_code_source(self) -> None:
|
def test_single_question_with_embedded_source_source(self) -> None:
|
||||||
self.args.question = ["What is this?"]
|
self.args.ask = ["What is this?"]
|
||||||
self.args.source = [f"{self.source_file2.name}"]
|
self.args.source = [f"{self.source_file2.name}"]
|
||||||
message = create_message(self.chat, self.args)
|
message = create_message(self.chat, self.args)
|
||||||
self.assertIsInstance(message, Message)
|
self.assertIsInstance(message, Message)
|
||||||
# source file contains 1 source code block
|
# source file contains 1 source code block
|
||||||
# -> expect it in the question
|
# -> expect it in the question
|
||||||
self.assertEqual(len(message.question.source_code()), 1)
|
self.assertEqual(len(message.question.source_code()), 1)
|
||||||
self.assertEqual(message.question, Question("""What is this?
|
self.assertEqual(message.question, Question(f"""What is this?
|
||||||
|
|
||||||
{self.source_file2_content}"""))
|
{self.source_file2_content}"""))
|
||||||
|
|
||||||
|
def test_single_question_with_embedded_source_code_source(self) -> None:
|
||||||
|
self.args.ask = ["What is this?"]
|
||||||
|
self.args.source_code = [f"{self.source_file2.name}"]
|
||||||
|
message = create_message(self.chat, self.args)
|
||||||
|
self.assertIsInstance(message, Message)
|
||||||
|
# source file contains 1 source code block
|
||||||
|
# -> expect it in the question
|
||||||
|
self.assertEqual(len(message.question.source_code()), 2)
|
||||||
|
self.assertEqual(message.question, Question(f"""What is this?
|
||||||
|
|
||||||
|
```
|
||||||
|
{self.source_file2_content}
|
||||||
|
```"""))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user