14 KiB
14 KiB
Epic 4: LLM Summarization & Persistence
Goal: Integrate with the configured local Ollama instance to generate summaries for successfully scraped article text and fetched comments. Persist these summaries locally. Implement a stage testing utility for summarization.
Story List
Story 4.1: Implement Ollama Client Module
- User Story / Goal: As a developer, I want a client module to interact with the configured Ollama API endpoint via HTTP, handling requests and responses for text generation, so that summaries can be generated programmatically.
- Detailed Requirements:
- Prerequisite: Ensure a local Ollama instance is installed and running, accessible via the URL defined in
.env(OLLAMA_ENDPOINT_URL), and that the model specified in.env(OLLAMA_MODEL) has been downloaded (e.g., viaollama pull model_name). Instructions for this setup should be in the project README. - Create a new module:
src/clients/ollamaClient.ts. - Implement an async function
generateSummary(promptTemplate: string, content: string): Promise<string | null>. (Note: Parameter name changed for clarity) - Add configuration variables
OLLAMA_ENDPOINT_URL(e.g.,http://localhost:11434) andOLLAMA_MODEL(e.g.,llama3) to.env.example. Ensure they are loaded via the config module (src/utils/config.ts). Update local.envwith actual values. Add optionalOLLAMA_TIMEOUT_MSto.env.examplewith a default like120000. - Inside
generateSummary:- Construct the full prompt string using the
promptTemplateand the providedcontent(e.g., replacing a placeholder like{Content Placeholder}in the template, or simple concatenation if templates are basic). - Construct the Ollama API request payload (JSON):
{ model: configured_model, prompt: full_prompt, stream: false }. Refer to Ollama/api/generatedocumentation anddocs/data-models.md. - Use native
Workspaceto send a POST request to the configured Ollama endpoint +/api/generate. Set appropriate headers (Content-Type: application/json). Use the configuredOLLAMA_TIMEOUT_MSor a reasonable default (e.g., 2 minutes). - Handle
Workspaceerrors (network, timeout) usingtry...catch. - Check
response.ok. If not OK, log the status/error and returnnull. - Parse the JSON response from Ollama. Extract the generated text (typically in the
responsefield). Refer todocs/data-models.md. - Check for potential errors within the Ollama response structure itself (e.g., an
errorfield). - Return the extracted summary string on success. Return
nullon any failure. - Log key events: initiating request (mention model), receiving response, success, failure reasons, potentially request/response time using the logger.
- Construct the full prompt string using the
- Define necessary TypeScript types for the Ollama request payload and expected response structure in
src/types/ollama.ts(referenced indocs/data-models.md).
- Prerequisite: Ensure a local Ollama instance is installed and running, accessible via the URL defined in
- Acceptance Criteria (ACs):
- AC1: The
ollamaClient.tsmodule exists and exportsgenerateSummary. - AC2:
OLLAMA_ENDPOINT_URLandOLLAMA_MODELare defined in.env.example, loaded via config, and used by the client. OptionalOLLAMA_TIMEOUT_MSis handled. - AC3:
generateSummarysends a correctly formatted POST request (model, full prompt based on template and content, stream:false) to the configured Ollama endpoint/path using nativeWorkspace. - AC4: Network errors, timeouts, and non-OK API responses are handled gracefully, logged, and result in a
nullreturn (given the Prerequisite Ollama service is running). - AC5: A successful Ollama response is parsed correctly, the generated text is extracted, and returned as a string.
- AC6: Unexpected Ollama response formats or internal errors (e.g.,
{"error": "..."}) are handled, logged, and result in anullreturn. - AC7: Logs provide visibility into the client's interaction with the Ollama API.
- AC1: The
Story 4.2: Define Summarization Prompts
- User Story / Goal: As a developer, I want standardized base prompts for generating article summaries and HN discussion summaries documented centrally, ensuring consistent instructions are sent to the LLM.
- Detailed Requirements:
- Define two standardized base prompts (
ARTICLE_SUMMARY_PROMPT,DISCUSSION_SUMMARY_PROMPT) and document them indocs/prompts.md. - Ensure these prompts are accessible within the application code, for example, by defining them as exported constants in a dedicated module like
src/utils/prompts.ts, which reads from or mirrors the content indocs/prompts.md.
- Define two standardized base prompts (
- Acceptance Criteria (ACs):
- AC1: The
ARTICLE_SUMMARY_PROMPTtext is defined indocs/prompts.mdwith appropriate instructional content. - AC2: The
DISCUSSION_SUMMARY_PROMPTtext is defined indocs/prompts.mdwith appropriate instructional content. - AC3: The prompt texts documented in
docs/prompts.mdare available as constants or variables within the application code (e.g., viasrc/utils/prompts.ts) for use by the Ollama client integration.
- AC1: The
Story 4.3: Integrate Summarization into Main Workflow
- User Story / Goal: As a developer, I want to integrate the Ollama client into the main workflow to generate summaries for each story's scraped article text (if available) and fetched comments, using centrally defined prompts and handling potential comment length limits.
- Detailed Requirements:
- Modify the main execution flow in
src/index.tsorsrc/core/pipeline.ts. - Import
ollamaClient.generateSummaryand the prompt constants/variables (e.g., fromsrc/utils/prompts.ts, which reflectdocs/prompts.md). - Load the optional
MAX_COMMENT_CHARS_FOR_SUMMARYconfiguration value from.envvia the config utility. - Within the main loop iterating through stories (after article scraping/persistence in Epic 3):
- Article Summary Generation:
- Check if the
storyobject has non-nullarticleContent. - If yes: log "Attempting article summarization for story {storyId}", call
await generateSummary(ARTICLE_SUMMARY_PROMPT, story.articleContent), store the result (string or null) asstory.articleSummary, log success/failure. - If no: set
story.articleSummary = null, log "Skipping article summarization: No content".
- Check if the
- Discussion Summary Generation:
- Check if the
storyobject has a non-emptycommentsarray. - If yes:
- Format the
story.commentsarray into a single text block suitable for the LLM prompt (e.g., concatenatingcomment.textwith separators like---). - Check truncation limit: If
MAX_COMMENT_CHARS_FOR_SUMMARYis configured to a positive number and theformattedCommentsTextlength exceeds it, truncateformattedCommentsTextto the limit and log a warning: "Comment text truncated to {limit} characters for summarization for story {storyId}". - Log "Attempting discussion summarization for story {storyId}".
- Call
await generateSummary(DISCUSSION_SUMMARY_PROMPT, formattedCommentsText). (Pass the potentially truncated text) - Store the result (string or null) as
story.discussionSummary. Log success/failure.
- Format the
- If no: set
story.discussionSummary = null, log "Skipping discussion summarization: No comments".
- Check if the
- Modify the main execution flow in
- Acceptance Criteria (ACs):
- AC1: Running
npm run devexecutes steps from Epics 1-3, then attempts summarization using the Ollama client. - AC2: Article summary is attempted only if
articleContentexists for a story. - AC3: Discussion summary is attempted only if
commentsexist for a story. - AC4:
generateSummaryis called with the correct prompts (sourced consistently withdocs/prompts.md) and corresponding content (article text or formatted/potentially truncated comments). - AC5: If
MAX_COMMENT_CHARS_FOR_SUMMARYis set and comment text exceeds it, the text passed togenerateSummaryis truncated, and a warning is logged. - AC6: Logs clearly indicate the start, success, or failure (including null returns from the client) for both article and discussion summarization attempts per story.
- AC7: Story objects in memory now contain
articleSummary(string/null) anddiscussionSummary(string/null) properties.
- AC1: Running
Story 4.4: Persist Generated Summaries Locally
(No changes needed for this story based on recent decisions)
- User Story / Goal: As a developer, I want to save the generated article and discussion summaries (or null placeholders) to a local JSON file for each story, making them available for the email assembly stage.
- Detailed Requirements:
- Define the structure for the summary output file:
{storyId}_summary.json. Content example:{ "storyId": "...", "articleSummary": "...", "discussionSummary": "...", "summarizedAt": "ISO_TIMESTAMP" }. Note thatarticleSummaryanddiscussionSummarycan benull. - Import
fsandpathinsrc/index.tsorsrc/core/pipeline.tsif needed. - In the main workflow loop, after both summarization attempts (article and discussion) for a story are complete:
- Create a summary result object containing
storyId,articleSummary(string or null),discussionSummary(string or null), and the current ISO timestamp (new Date().toISOString()). Add this timestamp to the in-memorystoryobject as well (story.summarizedAt). - Get the full path to the date-stamped output directory.
- Construct the filename:
{storyId}_summary.json. - Construct the full file path using
path.join(). - Serialize the summary result object to JSON (
JSON.stringify(..., null, 2)). - Use
fs.writeFileSyncto save the JSON to the file, wrapping intry...catch. - Log the successful saving of the summary file or any file writing errors.
- Create a summary result object containing
- Define the structure for the summary output file:
- Acceptance Criteria (ACs):
- AC1: After running
npm run dev, the date-stamped output directory contains 10 files named{storyId}_summary.json. - AC2: Each
_summary.jsonfile contains valid JSON adhering to the defined structure. - AC3: The
articleSummaryfield contains the generated summary string if successful, otherwisenull. - AC4: The
discussionSummaryfield contains the generated summary string if successful, otherwisenull. - AC5: A valid ISO timestamp is present in the
summarizedAtfield. - AC6: Logs confirm successful writing of each summary file or report file system errors.
- AC1: After running
Story 4.5: Implement Stage Testing Utility for Summarization
(Changes needed to reflect prompt sourcing and optional truncation)
- User Story / Goal: As a developer, I want a separate script/command to test the LLM summarization logic using locally persisted data (HN comments, scraped article text), allowing independent testing of prompts and Ollama interaction.
- Detailed Requirements:
- Create a new standalone script file:
src/stages/summarize_content.ts. - Import necessary modules:
fs,path,logger,config,ollamaClient, prompt constants (e.g., fromsrc/utils/prompts.ts). - The script should:
- Initialize logger, load configuration (Ollama endpoint/model, output dir, optional
MAX_COMMENT_CHARS_FOR_SUMMARY). - Determine target date-stamped directory path.
- Find all
{storyId}_data.jsonfiles in the directory. - For each
storyIdfound:- Read
{storyId}_data.jsonto get comments. Format them into a single text block. - Attempt to read
{storyId}_article.txt. Handle file-not-found gracefully. Store content or null. - Call
ollamaClient.generateSummaryfor article text (if not null) usingARTICLE_SUMMARY_PROMPT. - Apply truncation logic: If comments exist, check
MAX_COMMENT_CHARS_FOR_SUMMARYand truncate the formatted comment text block if needed, logging a warning. - Call
ollamaClient.generateSummaryfor formatted comments (if comments exist) usingDISCUSSION_SUMMARY_PROMPT(passing potentially truncated text). - Construct the summary result object (with summaries or nulls, and timestamp).
- Save the result object to
{storyId}_summary.jsonin the same directory (using logic from Story 4.4), overwriting if exists. - Log progress (reading files, calling Ollama, truncation warnings, saving results) for each story ID.
- Read
- Initialize logger, load configuration (Ollama endpoint/model, output dir, optional
- Add script to
package.json:"stage:summarize": "ts-node src/stages/summarize_content.ts".
- Create a new standalone script file:
- Acceptance Criteria (ACs):
- AC1: The file
src/stages/summarize_content.tsexists. - AC2: The script
stage:summarizeis defined inpackage.json. - AC3: Running
npm run stage:summarize(afterstage:fetchandstage:scraperuns) reads_data.jsonand attempts to read_article.txtfiles from the target directory. - AC4: The script calls the
ollamaClientwith correct prompts (sourced consistently withdocs/prompts.md) and content derived only from the local files (requires Ollama service running per Story 4.1 prerequisite). - AC5: If
MAX_COMMENT_CHARS_FOR_SUMMARYis set and applicable, comment text is truncated before calling the client, and a warning is logged. - AC6: The script creates/updates
{storyId}_summary.jsonfiles in the target directory reflecting the results of the Ollama calls (summaries or nulls). - AC7: Logs show the script processing each story ID found locally, interacting with Ollama, and saving results.
- AC8: The script does not call Algolia API or the article scraper module.
- AC1: The file
Change Log
| Change | Date | Version | Description | Author |
|---|---|---|---|---|
| Integrate prompts.md refs | 2025-05-04 | 0.3 | Updated stories 4.2, 4.3, 4.5 | 3-Architect |
| Added Ollama Prereq Note | 2025-05-04 | 0.2 | Added note about local Ollama setup | 2-pm |
| Initial Draft | 2025-05-04 | 0.1 | First draft of Epic 4 | 2-pm |