File size: 2,699 Bytes
1d32142
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
from typing import Literal
from pydantic import BaseModel, Field
from langgraph.store.base import BaseStore
from langchain_core.runnables import RunnableConfig


class MessageClassifier(BaseModel):
    """Classification result for routing messages."""
    message_type: Literal["emotional", "logical", "charity_search", "donor_search", "volunteer_search"] = Field(
        ...,
        description="Classify message for routing to appropriate agent."
    )


async def classify_message(state: dict, config: RunnableConfig, *, store: BaseStore, llm) -> dict:
    """Classify user message to route to appropriate agent.

    Args:
        state: Graph state containing messages
        config: Runtime config with user_id, thread_id
        store: Memory store (required by graph but not used here)
        llm: Language model instance

    Returns:
        Dict with message_type for routing
    """
    last_message = state["messages"][-1]
    classifier_llm = llm.with_structured_output(MessageClassifier)

    result = classifier_llm.invoke([
        {
            "role": "system",
            "content": """Classify the user message into one of these categories:

Respond ONLY with valid JSON in this exact format:
{"message_type": "TYPE"}

Where TYPE is one of:
- 'emotional': Message requires emotional support, therapy, deals with feelings, or personal problems
- 'donor_search': Looking for donors in the database, finding people who donate, matching donors by criteria
- 'volunteer_search': Looking for volunteers in the database, finding people who volunteer, matching volunteers
- 'charity_search': Asking about charity organizations, nonprofits, wanting to research specific charities
- 'logical': Facts, information, logical analysis, practical solutions (default for general queries)

Examples:
- "Find donors interested in education in Singapore" → donor_search
- "Show me volunteers with tech skills" → volunteer_search  
- "Tell me about Red Cross charity" → charity_search
- "I'm feeling sad today" → emotional
- "What is the capital of France?" → logical"""
        },
        {
            "role": "user",
            "content": last_message.content
        }
    ])
    return {"message_type": result.message_type}


def create_classifier(llm):
    """Factory to create classifier function with LLM bound.

    Usage:
        llm = ChatOllama(model="gpt-oss:120b-cloud")
        classify = create_classifier(llm)
        graph_builder.add_node("classifier", classify)
    """
    async def classifier_node(state: dict, config: RunnableConfig, *, store: BaseStore):
        return await classify_message(state, config, store=store, llm=llm)

    return classifier_node