Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3ea1628
Merge pull request #260 from Edge-Intelligence-Lab/main
Ar-temis Apr 15, 2026
a78cdc4
upgraded agent to take positional argument as a user query.
pomegranar Apr 15, 2026
a952666
added a cool TUI for agent
pomegranar Apr 15, 2026
e1d42e3
llama-index dependency bump (fixes warnings on agent run)
pomegranar Apr 15, 2026
1b612f3
Updating 225 to the latest version
Ar-temis Apr 16, 2026
f7eee1c
Added a logo to TUI after startup sequence.
pomegranar Apr 16, 2026
091f9f1
deleted 2-year-old log file nohup.out
pomegranar Apr 16, 2026
1db485e
Ignore CSV files
Ar-temis Apr 16, 2026
eb194a6
added thefuzz and pymupdf4llm to toml and added a few reasons to the …
Ar-temis Apr 16, 2026
4897198
feat(225): agentic course recommendations with policy-aware planning
pomegranar Apr 16, 2026
46e2529
style: black + flake8 cleanup on new files
pomegranar Apr 16, 2026
0282677
black formatted
pomegranar Apr 16, 2026
cd6ac07
Renamed query lookup to syllabi lookus
Ar-temis Apr 16, 2026
d1bfbfe
Refactor - Removed unnecessary tool outer functions
Ar-temis Apr 16, 2026
544e54e
fused both the assessment and executor together
Ar-temis Apr 21, 2026
c06ac34
Updated the model's configs and disabled thinking.
Ar-temis Apr 21, 2026
9bb3df5
Only 3 conversation exchanges are saved in history and rest is
Ar-temis Apr 21, 2026
f13fe9e
conversation summary before the history since it is the earlier excha…
Ar-temis Apr 21, 2026
a4467e5
Removed assessment and infused it into the executor call
Ar-temis Apr 21, 2026
e96b56c
used Path() classes for input paths
Ar-temis Apr 21, 2026
d714ec5
Added back in thought
Ar-temis Apr 21, 2026
0eb00e1
Merge branch 'main' into 225-course-recs
Ar-temis Apr 21, 2026
ce206cb
Modify Flake8 ignore rules in lint.yml
Ar-temis Apr 21, 2026
8f5797d
Fixing black and flake8 complaints
Ar-temis Apr 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,4 @@ jobs:
- name: Run Flake8 on changed files
if: needs.changed-files.outputs.files != ''
run: |
flake8 --ignore=E203,W503 --max-line-length 120 ${{ needs.changed-files.outputs.files }}
flake8 --ignore=E203,W503,E402 --max-line-length 120 ${{ needs.changed-files.outputs.files }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
.idea/
.vscode/

*.CSV
*.csv

# Embeddings cache
pipeline_cache
# Byte-compiled / optimized / DLL files
Expand Down
4 changes: 2 additions & 2 deletions chatdku/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def _initialize_defaults(self):
db_host = _env("DB_HOST")
db_port = _env("DB_PORT")
db_name = _env("DB_NAME")
psql_uri = f"postgresql://{db_user}:{quote_plus(db_password)}@{db_host}:{db_port}/{db_name}"
psql_uri = f"postgresql://{db_user}:{quote_plus(db_password or '')}@{db_host}:{db_port}/{db_name}"

self._store.update(
{
Expand Down Expand Up @@ -117,7 +117,7 @@ def _initialize_defaults(self):
# External data
"prereq_csv_path": "/datapool/chatdku_external_data/DK_SR_PREREQ_CRSE_CHATDKU.csv",
"classdata_csv_path": "/datapool/chatdku_external_data/cleaned_classdata.csv",
"major_requirements_dir": "/datapool/chatdku_external_data/doc_testing/output/ug_bulletin_2023-2024",
"major_req_dir": "/datapool/chatdku_external_data/doc_testing/output/ug_bulletin_2023-2024",
}
)
# refresh read-only view
Expand Down
112 changes: 68 additions & 44 deletions chatdku/core/agent.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
#!/usr/bin/env python3
import argparse
import os
import sys
import traceback
import pyfiglet

# Must be set before `import dspy` — prevents litellm from fetching the remote
# model pricing database at startup (cuts ~40s off cold-start time).
os.environ.setdefault("LITELLM_LOCAL_MODEL_COST_MAP", "True") # noqa: E402,E401
import dspy
from openinference.semconv.trace import OpenInferenceSpanKindValues, SpanAttributes
from opentelemetry.trace import Status, StatusCode, use_span
Expand All @@ -10,15 +17,16 @@
from chatdku.core.dspy_classes.executor import Executor
from chatdku.core.dspy_classes.plan import Planner
from chatdku.core.dspy_classes.synthesizer import Synthesizer
from chatdku.core.tools.course_schedule import CourseScheduleLookupOuter
from chatdku.core.tools.get_prerequisites import PrerequisiteLookupOuter
from chatdku.core.tools.course_recommender import CourseRecommender
from chatdku.core.tools.course_schedule import CourseScheduleLookup
from chatdku.core.tools.get_prerequisites import PrerequisiteLookup
from chatdku.core.tools.llama_index_tools import (
KeywordRetrieverOuter,
VectorRetrieverOuter,
)
from chatdku.core.tools.major_requirements import MajorRequirementsLookupOuter
from chatdku.core.tools.syllabi_tool.query_curriculum_db import QueryCurriculumOuter
from chatdku.core.utils import format_trajectory, load_conversation, span_start
from chatdku.core.tools.major_requirements import MajorRequirementsLookup
from chatdku.core.tools.syllabi.syllabi_tool import SyllabusLookupOuter
from chatdku.core.utils import load_conversation, span_start
from chatdku.setup import setup, use_phoenix

# When `--dev` is passed to the script, enable additional debug prints in this module.
Expand Down Expand Up @@ -99,18 +107,6 @@ def _forward_gen(
)

with use_span(span):
# Putting this in `self.__init__()` might not work due to that you might
# want DSPy to change prompt dynamically.

# These limits are for compressing both tool and conversation memory.
# Uses the executor's token limits as the executor has the largest context needs.
limits = self.executor.get_token_limits(
plan="",
current_user_message=current_user_message,
conversation_history=self.conversation_memory.history_str(),
trajectory=format_trajectory({}),
)

# Clear internal memory for each user message
self.internal_memory.clear()

Expand All @@ -125,7 +121,6 @@ def _forward_gen(
self.conversation_memory(
role="assistant",
content=prev_response,
max_history_size=limits["conversation_history"],
)

plan_result = self.planner(
Expand Down Expand Up @@ -157,7 +152,6 @@ def _forward_gen(
self.conversation_memory(
role="user",
content=current_user_message,
max_history_size=limits["conversation_history"],
)

if not self.streaming:
Expand Down Expand Up @@ -188,7 +182,8 @@ def forward(
return i


def main():
def build_agent(streaming: bool = True, max_iterations: int = 10) -> "Agent":
"""Configure DSPy and return a ready-to-use Agent instance."""
setup()
use_phoenix()

Expand All @@ -210,17 +205,13 @@ def main():
enable_thinking=False,
)
dspy.configure(lm=lm)
# To disable cache:

# To disable cache:
# dspy.configure_cache(
# enable_disk_cache=False,
# enable_memory_cache=False
# )

import time

# role = "student"
# access_type = "student" # hard code it for now, need parameter pass from user role
user_id = "Chat_DKU"
search_mode = 0
tools = [
Expand All @@ -240,29 +231,62 @@ def main():
search_mode=search_mode,
files=[],
),
# DocRetrieverOuter(
# retriever_top_k=25,
# use_reranker=True,
# reranker_top_n=5,
# access_type=access_type,
# role=role,
# user_id=user_id,
# search_mode=search_mode,
# files=[],
# ),
MajorRequirementsLookupOuter(config.major_requirements_dir),
QueryCurriculumOuter(),
PrerequisiteLookupOuter(prereq_csv_path=config.prereq_csv_path),
CourseScheduleLookupOuter(classdata_csv_path=config.classdata_csv_path),
SyllabusLookupOuter(),
MajorRequirementsLookup,
PrerequisiteLookup,
CourseRecommender,
CourseScheduleLookup,
]

agent = Agent(
max_iterations=3,
streaming=True,
return Agent(
max_iterations=max_iterations,
streaming=streaming,
get_intermediate=False,
tools=tools,
)


def run_query(query: str, agent: "Agent | None" = None) -> str:
"""Run a single query and return the full response as a string.

Suitable for programmatic use from Python:
from chatdku.core.agent import run_query
print(run_query("What are the CS major requirements?"))
"""
if agent is None:
agent = build_agent(streaming=False)
result = agent(current_user_message=query)
response = result.response
if isinstance(response, str):
return response
# Streaming generator — collect.
return "".join(response)


def main():
parser = argparse.ArgumentParser(description="ChatDKU agent.")
parser.add_argument(
"query",
nargs="*",
help="Query to run once and exit. If omitted, starts interactive mode.",
)
args = parser.parse_args()

if args.query:
query = " ".join(args.query)
print(run_query(query))
return

_main_interactive()


def _main_interactive():
import time

agent = build_agent(streaming=True)

pyfiglet.figlet_format("ChatDKU", font="slant")

while True:
try:
print("*" * 10)
Expand Down Expand Up @@ -290,5 +314,5 @@ def main():
main()
except Exception:
print(traceback.format_exc())

input()
if sys.stdin.isatty():
input()
13 changes: 13 additions & 0 deletions chatdku/core/ascii_logo
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
                             
                             
              **             
            .xxxx            
           -xxxxxx           
          =###*x###.         
         =###+  *###-        
        +###=    *###=       
      ---+x=      +x====     
     ------.      .======    
    --------.    =========.  
   ----------.  ===========. 
 .------------.-============.
Loading
Loading