Skip to content

Commit c8c5401

Browse files
committed
feat(ui): add dockerized postgres support and env password mgmt
1 parent c83e1ce commit c8c5401

File tree

1 file changed

+51
-22
lines changed

1 file changed

+51
-22
lines changed

codetide/agents/tide/ui/app.py

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
from aicore.config import Config
1010
from aicore.llm import Llm, LlmConfig
1111
from aicore.models import AuthenticationError, ModelError
12-
from aicore.const import STREAM_END_TOKEN, STREAM_START_TOKEN#, REASONING_START_TOKEN, REASONING_STOP_TOKEN
13-
from codetide.agents.tide.ui.utils import process_thread, run_concurrent_tasks, send_reasoning_msg
12+
from aicore.const import STREAM_END_TOKEN, STREAM_START_TOKEN#, REASONING_START_TOKEN, REASONING_STOP_TOKEN
13+
from codetide.agents.tide.ui.utils import process_thread, run_concurrent_tasks, send_reasoning_msg, check_docker, launch_postgres
1414
from codetide.agents.tide.ui.stream_processor import StreamProcessor, MarkerConfig
1515
from codetide.agents.tide.ui.defaults import AGENT_TIDE_PORT, STARTERS
1616
from codetide.agents.tide.ui.agent_tide_ui import AgentTideUi
@@ -24,29 +24,33 @@
2424
except ImportError as e:
2525
raise ImportError(
2626
"The 'codetide.agents' module requires the 'aicore' and 'chainlit' packages. "
27-
"Install it with: pip install codetide[agents-ui]"
27+
"Install it with: pip install codetide[aasygents-ui]"
2828
) from e
2929

3030
from codetide.agents.tide.ui.defaults import AICORE_CONFIG_EXAMPLE, EXCEPTION_MESSAGE, MISSING_CONFIG_MESSAGE
3131
from codetide.agents.tide.defaults import DEFAULT_AGENT_TIDE_LLM_CONFIG_PATH
3232
from codetide.core.defaults import DEFAULT_ENCODING
33+
from dotenv import get_key, load_dotenv, set_key
3334
from codetide.agents.data_layer import init_db
3435
from ulid import ulid
3536
import argparse
3637
import getpass
3738
import asyncio
39+
import secrets
40+
import string
3841
import json
3942
import yaml
4043
import time
4144

42-
@cl.password_auth_callback
43-
def auth():
44-
username = getpass.getuser()
45-
return cl.User(identifier=username, display_name=username)
45+
if check_docker and os.getenv("AGENTTIDE_PG_CONN_STR") is not None:
46+
@cl.password_auth_callback
47+
def auth():
48+
username = getpass.getuser()
49+
return cl.User(identifier=username, display_name=username)
4650

47-
@cl.data_layer
48-
def get_data_layer():
49-
return SQLAlchemyDataLayer(conninfo=f"sqlite+aiosqlite:///{os.environ['CHAINLIT_APP_ROOT']}/database.db")
51+
@cl.data_layer
52+
def get_data_layer():
53+
return SQLAlchemyDataLayer(conninfo=os.getenv("AGENTTIDE_PG_CONN_STR"))
5054

5155
@cl.on_settings_update
5256
async def setup_llm_config(settings):
@@ -442,9 +446,18 @@ async def agent_loop(message: Optional[cl.Message]=None, codeIdentifiers: Option
442446
chat_history.append({"role": "user", "content": feedback})
443447
await agent_loop(agent_tide_ui=agent_tide_ui)
444448

445-
# def generate_temp_password(length=16):
446-
# characters = string.ascii_letters + string.digits + string.punctuation
447-
# return ''.join(secrets.choice(characters) for _ in range(length))
449+
def generate_password(length: int = 16) -> str:
450+
"""
451+
Generate a secure random password.
452+
Works on Linux, macOS, and Windows.
453+
"""
454+
if password := get_key(Path(os.environ['CHAINLIT_APP_ROOT']) / ".env", "AGENTTDE_PG_PASSWORD"):
455+
return password
456+
457+
safe_chars = string.ascii_letters + string.digits + '-_@#$%^&*+=[]{}|:;<>?'
458+
password = ''.join(secrets.choice(safe_chars) for _ in range(length))
459+
set_key(Path(os.environ['CHAINLIT_APP_ROOT']) / ".env","AGENTTDE_PG_PASSWORD", password)
460+
return password
448461

449462
def serve(
450463
host=None,
@@ -454,14 +467,7 @@ def serve(
454467
ssl_keyfile=None,
455468
ws_per_message_deflate="true",
456469
ws_protocol="auto"
457-
):
458-
username = getpass.getuser()
459-
GREEN = "\033[92m"
460-
RESET = "\033[0m"
461-
462-
print(f"\n{GREEN}Your chainlit username is `{username}`{RESET}\n")
463-
464-
470+
):
465471
# if not os.getenv("_PASSWORD"):
466472
# temp_password = generate_temp_password()
467473
# os.environ["_PASSWORD"] = temp_password
@@ -513,10 +519,33 @@ def main():
513519
parser.add_argument("--config-path", type=str, default=DEFAULT_AGENT_TIDE_LLM_CONFIG_PATH, help="Path to the config file")
514520
args = parser.parse_args()
515521

522+
load_dotenv()
516523
os.environ["AGENT_TIDE_PROJECT_PATH"] = str(Path(args.project_path))
517524
os.environ["AGENT_TIDE_CONFIG_PATH"] = str(Path(args.project_path) / args.config_path)
525+
526+
load_dotenv()
527+
username = getpass.getuser()
528+
GREEN = "\033[92m"
529+
RED = "\033[91m"
530+
RESET = "\033[0m"
531+
532+
print(f"\n{GREEN}Your chainlit username is `{username}`{RESET}\n")
533+
534+
if check_docker():
535+
password = generate_password()
536+
launch_postgres(username, password, f"{os.environ['CHAINLIT_APP_ROOT']}/pgdata")
537+
538+
conn_string = f"postgresql+asyncpg://{username}:{password}@localhost:{os.getenv('AGENTTIDE_PG_PORT', 5437)}/agenttidedb"
539+
os.environ["AGENTTIDE_PG_CONN_STR"] = conn_string
540+
asyncio.run(init_db(os.environ["AGENTTIDE_PG_CONN_STR"]))
518541

519-
asyncio.run(init_db(f"{os.environ['CHAINLIT_APP_ROOT']}/database.db"))
542+
print(f"{GREEN} PostgreSQL launched on port {os.getenv('AGENTTIDE_PG_PORT', 5437)}{RESET}")
543+
print(f"{GREEN} Connection string stored in env var: AGENTTIDE_PG_CONN_STR{RESET}\n")
544+
else:
545+
print(f"{RED} Could not find Docker on this system.{RESET}")
546+
print(" PostgreSQL could not be launched for persistent data storage.")
547+
print(" You won't have access to multiple conversations or history beyond each session.")
548+
print(" Consider installing Docker and ensuring it is running.\n")
520549

521550
serve(
522551
host=args.host,

0 commit comments

Comments
 (0)