|
| 1 | +import json |
| 2 | +import logging |
| 3 | +from pprint import pformat |
| 4 | + |
| 5 | +import hayhooks |
| 6 | +from hayhooks import BasePipelineWrapper |
| 7 | +from haystack import Pipeline |
| 8 | +from haystack.components.builders import ChatPromptBuilder |
| 9 | +from haystack.dataclasses.chat_message import ChatMessage |
| 10 | +from haystack_integrations.components.generators.amazon_bedrock import AmazonBedrockChatGenerator |
| 11 | +from pydantic import BaseModel |
| 12 | +from sqlalchemy.inspection import inspect |
| 13 | + |
| 14 | +from src.app_config import config |
| 15 | +from src.db.models.support_listing import Support |
| 16 | + |
| 17 | +logger = logging.getLogger(__name__) |
| 18 | + |
| 19 | + |
| 20 | +class Resource(BaseModel): |
| 21 | + resource_name: str |
| 22 | + resource_addresses: list[str] |
| 23 | + resource_phones: list[str] |
| 24 | + description: str |
| 25 | + justification: str |
| 26 | + |
| 27 | + |
| 28 | +resource_as_json = json.dumps(Resource.model_json_schema(), indent=2) |
| 29 | + |
| 30 | +system_prompt = """ |
| 31 | +You are a supporting API for Goodwill Central Texas Referral. You are designed to help career case managers provide high-quality, local resource referrals to client's in Central Texas. |
| 32 | +Your role is to support Goodwill Central Texas career case managers working with low-income job seekers and learners in Austin and surrounding counties (Bastrop, Blanco, Burnet, Caldwell, DeWitt, Fayette, Gillespie, Gonzales, Hays, Lavaca, Lee, Llano, Mason, Travis, Williamson). |
| 33 | +
|
| 34 | +## Task Checklist |
| 35 | + - Evaluate the client needs and determine their eligibility (Factors to consider: age, income, disability, immigration/veteran status, number of dependents) |
| 36 | + - Prioritize Goodwill resources first (Basic Needs Resource packet, Goodwill websites) |
| 37 | + - Rank recommendations by proximity, eligibility fit, and other relevant factors |
| 38 | +
|
| 39 | +## Core Instructions |
| 40 | + - Use only trusted and up-to-date sources: Goodwill, government, vetted nonprofits, trusted news outlets (Findhelp, 211, Connect ATX permitted). Never use unreliable websites (e.g., shelterlistings.org, needhelppayingbills.com). |
| 41 | + - Never invent or fabricate resources. If none are available, state this clearly and suggest actionable, specific next steps |
| 42 | +
|
| 43 | +List of resources to choose from: |
| 44 | +{% for s in supports %} |
| 45 | + - {{ s.content }} |
| 46 | +{% endfor %} |
| 47 | +
|
| 48 | +## Response Constraints |
| 49 | + - Your response should ONLY include resources. |
| 50 | + - Do not summarize your assessment of the clients needs. |
| 51 | + - Limit the description for a resource to be less than 255 words. |
| 52 | + - Return a JSON list of resources in the following format: |
| 53 | + '''{{ resource_json }}''' |
| 54 | + """ |
| 55 | +model = "us.anthropic.claude-3-5-sonnet-20241022-v2:0" |
| 56 | + |
| 57 | +prompt_template = [ |
| 58 | + ChatMessage.from_system(system_prompt), |
| 59 | + ChatMessage.from_user("""User query: {{ query }}"""), |
| 60 | +] |
| 61 | + |
| 62 | + |
| 63 | +class PipelineWrapper(BasePipelineWrapper): |
| 64 | + name = "generate_referrals" |
| 65 | + |
| 66 | + def setup(self) -> None: |
| 67 | + pipeline = Pipeline() |
| 68 | + pipeline.add_component("llm", AmazonBedrockChatGenerator(model=model)) |
| 69 | + pipeline.add_component( |
| 70 | + instance=ChatPromptBuilder( |
| 71 | + template=prompt_template, required_variables=["query", "supports", "resource_json"] |
| 72 | + ), |
| 73 | + name="prompt_builder", |
| 74 | + ) |
| 75 | + pipeline.connect("prompt_builder", "llm.messages") |
| 76 | + |
| 77 | + self.pipeline = pipeline |
| 78 | + |
| 79 | + # Called for the `generate-referrals/run` endpoint |
| 80 | + def run_api(self, query: str) -> dict: |
| 81 | + supports_from_db = retrieve_supports_from_db() |
| 82 | + response = self.pipeline.run( |
| 83 | + { |
| 84 | + "prompt_builder": { |
| 85 | + "query": query, |
| 86 | + "supports": supports_from_db, |
| 87 | + "resource_json": resource_as_json, |
| 88 | + }, |
| 89 | + } |
| 90 | + ) |
| 91 | + logger.info("Results: %s", pformat(response)) |
| 92 | + return response |
| 93 | + |
| 94 | + # https://docs.haystack.deepset.ai/docs/hayhooks#openai-compatibility |
| 95 | + # Called for the `{pipeline_name}/chat`, `/chat/completions`, or `/v1/chat/completions` streaming endpoint using Server-Sent Events (SSE) |
| 96 | + def run_chat_completion(self, model: str, messages: list, body: dict) -> None: |
| 97 | + logger.info("Running chat completion with model: %s, messages: %s", model, messages) |
| 98 | + question = hayhooks.get_last_user_message(messages) |
| 99 | + logger.info("Question: %s", question) |
| 100 | + return hayhooks.streaming_generator( |
| 101 | + pipeline=self.pipeline, |
| 102 | + pipeline_run_args={ |
| 103 | + "echo_component": { |
| 104 | + "prompt": [ChatMessage.from_user(question)], |
| 105 | + "history": messages[:-1], |
| 106 | + } |
| 107 | + }, |
| 108 | + ) |
| 109 | + |
| 110 | + |
| 111 | +def retrieve_supports_from_db() -> list[str]: |
| 112 | + all_supports: list[str] = [] |
| 113 | + with config.db_session() as db_session, db_session.begin(): |
| 114 | + all_db_supports = db_session.query(Support).all() |
| 115 | + |
| 116 | + for support in all_db_supports: |
| 117 | + support_dict = { |
| 118 | + c.key: getattr(support, c.key) for c in inspect(Support).mapper.column_attrs |
| 119 | + } |
| 120 | + support_as_str = json.dumps(support_dict, default=str) |
| 121 | + all_supports.append(support_as_str) |
| 122 | + return all_supports |
0 commit comments