das-cli 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- das/__init__.py +0 -0
- das/ai/plugins/dasai.py +50 -0
- das/ai/plugins/entries/entries_plugin.py +16 -0
- das/app.py +37 -0
- das/authentication/auth.py +43 -0
- das/authentication/secure_input.py +67 -0
- das/cli.py +1070 -0
- das/common/api.py +100 -0
- das/common/config.py +185 -0
- das/common/entry_fields_constants.py +3 -0
- das/common/enums.py +46 -0
- das/common/file_utils.py +203 -0
- das/managers/__init__.py +0 -0
- das/managers/download_manager.py +93 -0
- das/managers/entries_manager.py +433 -0
- das/managers/search_manager.py +64 -0
- das/services/attributes.py +81 -0
- das/services/cache.py +70 -0
- das/services/downloads.py +84 -0
- das/services/entries.py +132 -0
- das/services/entry_fields.py +33 -0
- das/services/hangfire.py +26 -0
- das/services/search.py +33 -0
- das_cli-1.0.0.dist-info/METADATA +408 -0
- das_cli-1.0.0.dist-info/RECORD +29 -0
- das_cli-1.0.0.dist-info/WHEEL +5 -0
- das_cli-1.0.0.dist-info/entry_points.txt +2 -0
- das_cli-1.0.0.dist-info/licenses/LICENSE +22 -0
- das_cli-1.0.0.dist-info/top_level.txt +1 -0
das/__init__.py
ADDED
|
File without changes
|
das/ai/plugins/dasai.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
import asyncio
|
|
5
|
+
from semantic_kernel import Kernel
|
|
6
|
+
from semantic_kernel.connectors.ai.open_ai import (
|
|
7
|
+
OpenAIChatCompletion,
|
|
8
|
+
)
|
|
9
|
+
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
|
|
10
|
+
from semantic_kernel.contents.chat_history import ChatHistory
|
|
11
|
+
from semantic_kernel.connectors.ai.function_choice_behavior import FunctionChoiceBehavior
|
|
12
|
+
from das.ai.plugins.entries.entries_plugin import GetEntryByCodePlugin
|
|
13
|
+
from das.common.config import load_openai_api_key
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
load_dotenv()
|
|
17
|
+
|
|
18
|
+
class DasAI:
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
self.kernel = Kernel()
|
|
22
|
+
api_key = os.getenv("OPENAI_API_KEY") or load_openai_api_key()
|
|
23
|
+
if not api_key:
|
|
24
|
+
raise ValueError("OpenAI API key is not configured.")
|
|
25
|
+
self.openai_chat_completion = OpenAIChatCompletion(ai_model_id="gpt-4o-mini", api_key=api_key)
|
|
26
|
+
self.kernel.add_service(self.openai_chat_completion)
|
|
27
|
+
self.kernel.add_plugin(GetEntryByCodePlugin(), plugin_name="get_entry_by_code")
|
|
28
|
+
|
|
29
|
+
async def main(self):
|
|
30
|
+
history = ChatHistory()
|
|
31
|
+
history.add_system_message("You are a laboratory assistent that helps researchers; phD students and any employee of NIOZ to find their answers in DAS (Data Archive System). Only answer questions about DAS.")
|
|
32
|
+
user_input = None
|
|
33
|
+
while user_input != "exit":
|
|
34
|
+
user_input = input("User > ")
|
|
35
|
+
history.add_user_message(user_input)
|
|
36
|
+
result = await self.openai_chat_completion.get_chat_message_content(
|
|
37
|
+
chat_history=history,
|
|
38
|
+
settings=PromptExecutionSettings(
|
|
39
|
+
api_key=os.getenv("OPENAI_API_KEY") or load_openai_api_key(),
|
|
40
|
+
function_choice_behavior=FunctionChoiceBehavior.Auto()
|
|
41
|
+
),
|
|
42
|
+
kernel=self.kernel
|
|
43
|
+
)
|
|
44
|
+
print("Assistant >",str(result))
|
|
45
|
+
history.add_message(result)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if __name__ == "__main__":
|
|
49
|
+
ai = DasAI()
|
|
50
|
+
asyncio.run(ai.main())
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from semantic_kernel.functions.kernel_function_decorator import kernel_function
|
|
3
|
+
from semantic_kernel.functions.kernel_arguments import KernelArguments
|
|
4
|
+
from das.managers.entries_manager import EntryManager
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class GetEntryByCodePlugin:
|
|
10
|
+
def __init__(self):
|
|
11
|
+
self.entry_manager = EntryManager()
|
|
12
|
+
|
|
13
|
+
@kernel_function(name="GetEntryByCode", description="Get an entry by its code.")
|
|
14
|
+
def get_entry_by_code(self, code: str):
|
|
15
|
+
"""Get the entry code based on the entry name."""
|
|
16
|
+
return self.entry_manager.get(code=code)
|
das/app.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from das.authentication.auth import Auth
|
|
2
|
+
from das.services.attributes import AttributesService
|
|
3
|
+
from das.services.cache import CacheService
|
|
4
|
+
from das.services.entries import EntriesService
|
|
5
|
+
from das.services.entry_fields import EntryFieldsService
|
|
6
|
+
from das.services.hangfire import HangfireService
|
|
7
|
+
from das.services.search import SearchService
|
|
8
|
+
from das.common.config import save_api_url
|
|
9
|
+
|
|
10
|
+
# $env:PYTHONPATH="C:\Workspace\das-cli"
|
|
11
|
+
|
|
12
|
+
class Das:
|
|
13
|
+
def __init__(self, base_url: str):
|
|
14
|
+
if base_url is None or base_url == "":
|
|
15
|
+
raise ValueError("You must be authenticated.")
|
|
16
|
+
save_api_url(base_url)
|
|
17
|
+
self.base_url = base_url
|
|
18
|
+
self.attributes = AttributesService(base_url)
|
|
19
|
+
self.cache = CacheService(base_url)
|
|
20
|
+
self.entries = EntriesService(base_url)
|
|
21
|
+
self.hangfire = HangfireService(base_url)
|
|
22
|
+
self.entry_fields = EntryFieldsService(base_url)
|
|
23
|
+
self.search = SearchService(base_url)
|
|
24
|
+
|
|
25
|
+
def authenticate(self, username: str, password: str) -> str:
|
|
26
|
+
"""
|
|
27
|
+
Authenticate with the DAS API and return the token.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
username (str): The username to authenticate with
|
|
31
|
+
password (str): The password to authenticate with
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
str: The authentication token or None if authentication fails
|
|
35
|
+
"""
|
|
36
|
+
auth = Auth(self.base_url, username, password)
|
|
37
|
+
return auth.token
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from das.common.api import post_data
|
|
2
|
+
from das.common.config import save_token
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Auth:
|
|
6
|
+
def __init__(self, base_url, username, password):
|
|
7
|
+
self.base_url = base_url
|
|
8
|
+
self.username = username
|
|
9
|
+
# Don't store password as an instance variable for security
|
|
10
|
+
self.token = None
|
|
11
|
+
|
|
12
|
+
# Pass password directly to authenticate method
|
|
13
|
+
self.authenticate(password)
|
|
14
|
+
|
|
15
|
+
def authenticate(self, password):
|
|
16
|
+
"""
|
|
17
|
+
Authenticate the user and obtain an access token.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
password (str): The password to authenticate with
|
|
21
|
+
"""
|
|
22
|
+
url = f"{self.base_url}/api/TokenAuth/Authenticate"
|
|
23
|
+
data = {
|
|
24
|
+
"userNameOrEmailAddress": self.username,
|
|
25
|
+
"password": password
|
|
26
|
+
}
|
|
27
|
+
response = post_data(url, data=data)
|
|
28
|
+
if response.get("error"):
|
|
29
|
+
print(f"Authentication failed: {response['error']}")
|
|
30
|
+
else:
|
|
31
|
+
self.token = response.get("result").get("accessToken")
|
|
32
|
+
save_token(self.token)
|
|
33
|
+
# Return silently, let the CLI handle success messages
|
|
34
|
+
return self.token
|
|
35
|
+
|
|
36
|
+
if __name__ == "__main__":
|
|
37
|
+
# Example for testing authentication
|
|
38
|
+
import getpass
|
|
39
|
+
base_url = input("Base URL: ")
|
|
40
|
+
username = input("Username: ")
|
|
41
|
+
password = getpass.getpass("Password: ")
|
|
42
|
+
auth = Auth(base_url=base_url, username=username, password=password)
|
|
43
|
+
print(f"Token obtained: {bool(auth.token)}")
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import getpass
|
|
2
|
+
import keyring
|
|
3
|
+
import platform
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
# Define service name for keyring
|
|
7
|
+
SERVICE_NAME = "das-cli"
|
|
8
|
+
|
|
9
|
+
def get_password(username, prompt="Password: "):
|
|
10
|
+
"""
|
|
11
|
+
Get password securely using the appropriate method for the platform.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
username (str): Username for associating with stored password
|
|
15
|
+
prompt (str): Prompt text to display when asking for password
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
str: The password entered by user or retrieved from keyring
|
|
19
|
+
"""
|
|
20
|
+
try:
|
|
21
|
+
# Try to get password from keyring first
|
|
22
|
+
stored_pass = keyring.get_password(SERVICE_NAME, username)
|
|
23
|
+
if stored_pass:
|
|
24
|
+
return stored_pass
|
|
25
|
+
except:
|
|
26
|
+
# If keyring fails, continue to manual input
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
# Get password from user input
|
|
30
|
+
return getpass.getpass(prompt)
|
|
31
|
+
|
|
32
|
+
def store_credentials(username, password):
|
|
33
|
+
"""
|
|
34
|
+
Store credentials securely in the system's keyring.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
username (str): The username to store
|
|
38
|
+
password (str): The password to store
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
bool: True if successful, False otherwise
|
|
42
|
+
"""
|
|
43
|
+
try:
|
|
44
|
+
keyring.set_password(SERVICE_NAME, username, password)
|
|
45
|
+
return True
|
|
46
|
+
except Exception as e:
|
|
47
|
+
print(f"Warning: Could not store credentials securely ({e})")
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
def clear_stored_credentials(username):
|
|
51
|
+
"""
|
|
52
|
+
Remove stored credentials from the system's keyring.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
username (str): The username whose credentials should be removed
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
bool: True if successful, False otherwise
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
keyring.delete_password(SERVICE_NAME, username)
|
|
62
|
+
return True
|
|
63
|
+
except keyring.errors.PasswordDeleteError:
|
|
64
|
+
# Password doesn't exist
|
|
65
|
+
return True
|
|
66
|
+
except Exception:
|
|
67
|
+
return False
|