diff --git a/.flake8 b/.flake8 index 821a307..a7ac5ed 100644 --- a/.flake8 +++ b/.flake8 @@ -4,7 +4,7 @@ ignore = # line break after binary operator W504 per-file-ignores = - sample_cli/src/sample_cli/sample_cli_help.py:W605 + cyrano/cyrano_help.py:W605 exclude = env doc diff --git a/cyrano/cyrano.py b/cyrano/cyrano.py index b53e4b0..995e5b5 100644 --- a/cyrano/cyrano.py +++ b/cyrano/cyrano.py @@ -1,10 +1,6 @@ -from __future__ import print_function -from typing import Literal - from knack import CLI from cyrano.common.config import CLI_ENV_VARIABLE_PREFIX, GLOBAL_CONFIG_DIR from cyrano.common.logmodule import init_logging -from cyrano.groups.group1.greeting_exception import GreetingException from .cyrano_commands import CyranoCommandsLoader from .cyrano_help import CyranoHelp @@ -25,10 +21,3 @@ def __init__(self) -> None: help_cls=CyranoHelp) init_logging('cyrano.log') self.args = None - - def exception_handler(self, ex) -> Literal[1]: - if isinstance(ex, GreetingException): - print(f'Greetings caught: {ex}') - return 1 - - return super(Cyrano, self).exception_handler(ex) diff --git a/cyrano/cyrano_commands.py b/cyrano/cyrano_commands.py index 0babfef..380703f 100644 --- a/cyrano/cyrano_commands.py +++ b/cyrano/cyrano_commands.py @@ -1,8 +1,8 @@ from collections import OrderedDict from knack.commands import CLICommandsLoader -from cyrano.groups.group1._module import ( - load_commands as load_group1_commands, - load_arguments as load_group1_arguments +from cyrano.groups.optimize._module import ( + load_commands as load_optimize_commands, + load_arguments as load_optimize_arguments ) @@ -12,9 +12,9 @@ class CyranoCommandsLoader(CLICommandsLoader): """ def load_command_table(self, args) -> OrderedDict: - load_group1_commands(self) + load_optimize_commands(self) return super(CyranoCommandsLoader, self).load_command_table(args) def load_arguments(self, command) -> None: - load_group1_arguments(self) + load_optimize_arguments(self) super(CyranoCommandsLoader, self).load_arguments(command) diff --git a/cyrano/cyrano_help.py b/cyrano/cyrano_help.py index 71a45e7..e296b03 100644 --- a/cyrano/cyrano_help.py +++ b/cyrano/cyrano_help.py @@ -13,7 +13,7 @@ class CyranoHelp(CLIHelp): def __init__(self, cli_ctx=None) -> None: # import command group help - import cyrano.groups.group1._help # noqa: F401 + import cyrano.groups.optimize._help # noqa: F401 super(CyranoHelp, self).__init__( cli_ctx=cli_ctx, welcome_message=WELCOME_MESSAGE) diff --git a/cyrano/groups/group1/_help.py b/cyrano/groups/group1/_help.py deleted file mode 100644 index 6ef0882..0000000 --- a/cyrano/groups/group1/_help.py +++ /dev/null @@ -1,8 +0,0 @@ -from knack.help_files import helps - - -helps['group1'] = """ - type: group - short-summary: Commands for group1. - long-summary: -""" diff --git a/cyrano/groups/group1/commands.py b/cyrano/groups/group1/commands.py deleted file mode 100644 index c125840..0000000 --- a/cyrano/groups/group1/commands.py +++ /dev/null @@ -1,16 +0,0 @@ -import logging -from .greeting_exception import GreetingException - - -def greetings(name: str, throw_exception: bool = False) -> None: - """ - Sends greetings. - - :param name: the name to greet - :param throw_exception: send greetings as exception - """ - logging.info('Sending greetings') - greeting = f'Hello {name}!' - if throw_exception: - raise GreetingException(greeting) - print(greeting) diff --git a/cyrano/groups/group1/greeting_exception.py b/cyrano/groups/group1/greeting_exception.py deleted file mode 100644 index f3a07b4..0000000 --- a/cyrano/groups/group1/greeting_exception.py +++ /dev/null @@ -1,3 +0,0 @@ -class GreetingException(Exception): - def __init__(self, message) -> None: - super(GreetingException, self).__init__(message) diff --git a/cyrano/groups/group1/__init__.py b/cyrano/groups/optimize/__init__.py similarity index 100% rename from cyrano/groups/group1/__init__.py rename to cyrano/groups/optimize/__init__.py diff --git a/cyrano/groups/optimize/_help.py b/cyrano/groups/optimize/_help.py new file mode 100644 index 0000000..eed4fbf --- /dev/null +++ b/cyrano/groups/optimize/_help.py @@ -0,0 +1,8 @@ +from knack.help_files import helps + + +helps['optimize'] = """ + type: group + short-summary: Commands for optimizing resumes. + long-summary: +""" diff --git a/cyrano/groups/group1/_module.py b/cyrano/groups/optimize/_module.py similarity index 57% rename from cyrano/groups/group1/_module.py rename to cyrano/groups/optimize/_module.py index f06679e..9398b2c 100644 --- a/cyrano/groups/group1/_module.py +++ b/cyrano/groups/optimize/_module.py @@ -10,8 +10,8 @@ def load_commands(cli_command_loader) -> None: """ with CommandGroup(cli_command_loader, - 'group1', 'cyrano.groups.group1.{}') as group: - group.command('greetings', 'commands#greetings') + 'optimize', 'cyrano.groups.optimize.{}') as group: + group.command('experiences', 'commands#optimize_experiences') def load_arguments(cli_command_loader) -> None: @@ -22,5 +22,6 @@ def load_arguments(cli_command_loader) -> None: """ with ArgumentsContext(cli_command_loader, 'group1') as arguments: - arguments.argument('name', options_list='--name') - arguments.argument('throw_exception', options_list='--throw-exception') + arguments.argument('experiences_file', options_list='--experiences-file') + arguments.argument('requirements_file', options_list='--requirements-file') + arguments.argument('output_file', options_list='--output-file') diff --git a/cyrano/groups/optimize/commands.py b/cyrano/groups/optimize/commands.py new file mode 100644 index 0000000..c3a3c27 --- /dev/null +++ b/cyrano/groups/optimize/commands.py @@ -0,0 +1,62 @@ +import asyncio +import logging +from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion, AzureChatPromptExecutionSettings +from semantic_kernel.contents import ChatHistory +from ...common.config import GLOBAL_CONFIG + + +def optimize_experiences(experiences_file: str, + requirements_file: str, + output_file: str) -> None: + """ + Optimize the experiences based on the job requirements and + save the results in the output file. + + :param experiences_file: the file containing the experiences + :param requirements_file: the file containing the job requirements + :param output_file: the output file + """ + + asyncio.run(_optimize_experiences(experiences_file, requirements_file, output_file)) + + +async def _optimize_experiences(experiences_file: str, + requirements_file: str, + output_file: str) -> None: + logging.info('Optimize experiences') + + # read experiences and job requirements + with open(experiences_file, 'r', encoding='utf-8') as file: + experiences = file.read() + with open(requirements_file, 'r', encoding='utf-8') as file: + requirements = file.read() + + chat_completion_service = AzureChatCompletion( + deployment_name=GLOBAL_CONFIG.get('azure_openai', 'deployment'), + endpoint=GLOBAL_CONFIG.get('azure_openai', 'endpoint'), + api_key=GLOBAL_CONFIG.get('azure_openai', 'api_key')) + + request_settings = AzureChatPromptExecutionSettings( + max_tokens=1000, + ) + + system_message = """ + You are an assistant that helps people find the best + 3 experiences based on the job requirements. The user + will provide you with a list of experiences and a list + of requirements. + """ + + user_input = f"Requirements: {requirements}\n\n" \ + f"Experiences: {experiences}" + + chat_history = ChatHistory(system_message=system_message) + chat_history.add_user_message(user_input) + + response = await chat_completion_service.get_chat_message_content( + chat_history=chat_history, + settings=request_settings, + ) + if response: + with open(output_file, 'w', encoding='utf-8') as file: + file.write(response) diff --git a/docs/installation.md b/docs/installation.md index e7f284d..75b187a 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -24,5 +24,14 @@ cyrano Subgroups: - group1 : Commands for group1. + optimize : Commands for optimizing resumes. + ``` + +1. Update the `~/.cyrano/config` file with the Azure OpenAI values: + + ```text + [azure_openai] + deployment = + endpoint = + api_key = ``` diff --git a/pyproject.toml b/pyproject.toml index 6127c2a..c4d2ba1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ authors = [ ] dependencies = [ "knack >= 0.12.0", + "semantic-kernel >= 1.17.1", ] dynamic = ["version"] diff --git a/tests/foo_tests.py b/tests/foo_tests.py new file mode 100644 index 0000000..b8e4490 --- /dev/null +++ b/tests/foo_tests.py @@ -0,0 +1,7 @@ +import unittest + + +class FooTests(unittest.TestCase): + + def test_foo(self) -> None: + self.assertEqual('bar', 'bar') diff --git a/tests/group1_tests.py b/tests/group1_tests.py deleted file mode 100644 index db242ac..0000000 --- a/tests/group1_tests.py +++ /dev/null @@ -1,22 +0,0 @@ -import unittest -from cyrano.groups.group1.commands import greetings -from cyrano.groups.group1.greeting_exception import GreetingException -from unittest.mock import patch - - -class Group1Tests(unittest.TestCase): - - @patch('builtins.print') - def test_greet_me(self, mocked_print) -> None: - greetings('Vladimir', False) - mocked_print.assert_called_with("Hello Vladimir!") - - def test_greet_me_with_exception(self) -> None: - with self.assertRaises(GreetingException) as context: - greetings('Vladimir', True) - - self.assertEqual(str(context.exception), "Hello Vladimir!") - - -if __name__ == "__main__": - unittest.main()