Initialize Chronoscribe AI Project#14
Conversation
- Created `chronoscribe` directory with `src` and `tests` - Implemented `LLMClient` and `DBClient` to interact with existing Q-Mem stack - Implemented `ContentEngine` for basic content drafting - Added unit tests for all components - Added `requirements.txt` and updated `.gitignore`
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
q-mcp | b79b30c | Commit Preview URL Branch Preview URL |
Feb 22 2026, 08:24 PM |
| @@ -0,0 +1,41 @@ | |||
| import requests | |||
| import json | |||
| import requests | ||
| import json | ||
| import logging | ||
| import os |
| @@ -0,0 +1,26 @@ | |||
| import unittest | |||
| from unittest.mock import patch, MagicMock | |||
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
|
Igor Holt (@igor-holt) I've opened a new pull request, #16, to work on those changes. Once the pull request is ready, I'll request review from you. |
- Scaffolded `chronoscribe/` directory with `src` and `tests` - Implemented `ContentEngine`, `LLMClient`, and `DBClient` - Added unit tests and `requirements.txt` - Added `.wranglerignore` to exclude `chronoscribe/` and `models/` from Cloudflare Workers builds - Updated `.gitignore` to exclude `__pycache__` Co-authored-by: igor-holt <125706350+igor-holt@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces the Chronoscribe AI project, a content lifecycle engine that leverages the existing Genesis Q-Mem Stack (Phi-2 LLM and Redis cache) to draft content. The implementation provides a clean abstraction layer over the LLM server and Redis cache with appropriate separation of concerns.
Changes:
- Adds
ContentEngineclass for content drafting with LLM integration - Implements
LLMClientandDBClientwrapper classes for external dependencies - Includes comprehensive unit tests with mocked dependencies
Reviewed changes
Copilot reviewed 8 out of 11 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| chronoscribe/src/content_engine.py | Core content engine that orchestrates LLM generation and metadata storage |
| chronoscribe/src/llm_client.py | HTTP client wrapper for llama.cpp server API |
| chronoscribe/src/db_client.py | Redis client wrapper for content metadata persistence |
| chronoscribe/tests/test_content_engine.py | Unit tests for ContentEngine with mocked dependencies |
| chronoscribe/tests/test_clients.py | Unit tests for LLMClient and DBClient |
| chronoscribe/requirements.txt | Project dependencies (requests, redis) |
| chronoscribe/README.md | Project documentation describing purpose and infrastructure |
| .gitignore | Added Python-specific ignore patterns |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def generate(self, prompt, max_tokens=256, temperature=0.7): | ||
| """ | ||
| Generates text using the LLM server. | ||
| Assumes llama.cpp server API compatibility. | ||
| """ | ||
| url = f"{self.base_url}/completion" | ||
| payload = { | ||
| "prompt": prompt, | ||
| "n_predict": max_tokens, | ||
| "temperature": temperature, | ||
| "stop": ["User:", "\n\n"] |
There was a problem hiding this comment.
The hardcoded stop sequences include "\n\n" (double newline), which may prematurely truncate content generation for article-style content that naturally includes paragraph breaks (double newlines). Consider making the stop sequences configurable as an optional parameter to the generate method, or removing "\n\n" from the default stop sequences for content generation use cases.
| def generate(self, prompt, max_tokens=256, temperature=0.7): | |
| """ | |
| Generates text using the LLM server. | |
| Assumes llama.cpp server API compatibility. | |
| """ | |
| url = f"{self.base_url}/completion" | |
| payload = { | |
| "prompt": prompt, | |
| "n_predict": max_tokens, | |
| "temperature": temperature, | |
| "stop": ["User:", "\n\n"] | |
| def generate(self, prompt, max_tokens=256, temperature=0.7, stop_sequences=None): | |
| """ | |
| Generates text using the LLM server. | |
| Assumes llama.cpp server API compatibility. | |
| """ | |
| url = f"{self.base_url}/completion" | |
| stop = stop_sequences if stop_sequences is not None else ["User:"] | |
| payload = { | |
| "prompt": prompt, | |
| "n_predict": max_tokens, | |
| "temperature": temperature, | |
| "stop": stop, |
| import requests | ||
| import json | ||
| import logging | ||
| import os |
There was a problem hiding this comment.
The os module is imported but never used in this file. Consider removing this unused import to keep the code clean.
| import os |
| @@ -0,0 +1,41 @@ | |||
| import requests | |||
| import json | |||
There was a problem hiding this comment.
The json module is imported but never used in this file. Consider removing this unused import to keep the code clean.
| import json |
| @patch('chronoscribe.src.llm_client.requests.post') | ||
| def test_generate(self, mock_post): | ||
| mock_response = MagicMock() | ||
| mock_response.json.return_value = {"content": "Generated text"} | ||
| mock_post.return_value = mock_response | ||
|
|
||
| client = LLMClient() | ||
| result = client.generate("Test prompt") | ||
| self.assertEqual(result, "Generated text") |
There was a problem hiding this comment.
The test only covers the success path for the generate method. Consider adding test cases for error scenarios, such as when the HTTP request fails (raises an exception), when the response status is not successful (raise_for_status raises an exception), or when the response JSON doesn't contain the expected 'content' field. The implementation raises exceptions in error cases, and these should be tested.
| @patch('chronoscribe.src.db_client.redis.Redis') | ||
| def test_save_and_get_content(self, mock_redis): | ||
| mock_r = mock_redis.return_value | ||
| client = DBClient() | ||
|
|
||
| metadata = {"id": "123", "content": "test"} | ||
| client.save_content_metadata("123", metadata) | ||
| mock_r.set.assert_called_with("content:123", json.dumps(metadata)) |
There was a problem hiding this comment.
The test only covers the success path for save_content_metadata. Consider adding test cases for error scenarios where the Redis set operation fails and raises an exception, which should be tested since the implementation re-raises the exception.
| def draft_content(self, brief, content_type="article"): | ||
| """ | ||
| Drafts content based on a brief. | ||
| """ |
There was a problem hiding this comment.
The draft_content method accepts brief and content_type parameters without any validation. Consider adding basic input validation to handle edge cases such as None values or empty strings. For example, if brief is None or empty, the prompt construction on line 18 will still succeed but may result in poor quality LLM output. Similarly, an empty or None content_type would create a malformed prompt.
| """ | |
| """ | |
| # Basic input validation to prevent malformed prompts and metadata. | |
| if not isinstance(brief, str) or not brief.strip(): | |
| logger.error("Invalid 'brief' provided to draft_content; expected non-empty string.") | |
| raise ValueError("Parameter 'brief' must be a non-empty string.") | |
| if not isinstance(content_type, str) or not content_type.strip(): | |
| logger.error("Invalid 'content_type' provided to draft_content; expected non-empty string.") | |
| raise ValueError("Parameter 'content_type' must be a non-empty string.") | |
| # Normalize values after validation. | |
| brief = brief.strip() | |
| content_type = content_type.strip() |
| self.assertEqual(result['status'], "draft") | ||
|
|
||
| # Verify interactions | ||
| mock_llm.generate.assert_called_once() |
There was a problem hiding this comment.
The test verifies that generate is called once but doesn't verify the prompt argument passed to it. Consider using assert_called_once_with to verify that the correct prompt is constructed from the brief and content_type. This would ensure the prompt formatting is correct.
| @patch('chronoscribe.src.llm_client.requests.get') | ||
| def test_check_health(self, mock_get): | ||
| mock_get.return_value.status_code = 200 | ||
| client = LLMClient() | ||
| self.assertTrue(client.check_health()) | ||
|
|
||
| mock_get.return_value.status_code = 500 | ||
| self.assertFalse(client.check_health()) |
There was a problem hiding this comment.
The test covers success (status 200) and failure (status 500) cases, but doesn't test the exception handling path. Consider adding a test case where mock_get raises an exception (e.g., requests.RequestException) to verify that check_health returns False and logs the error correctly, as implemented in llm_client.py lines 16-18.
| @patch('chronoscribe.src.db_client.redis.Redis') | ||
| def test_check_health(self, mock_redis): | ||
| mock_r = mock_redis.return_value | ||
| mock_r.ping.return_value = True | ||
|
|
||
| client = DBClient() | ||
| self.assertTrue(client.check_health()) |
There was a problem hiding this comment.
The test only covers the success case for check_health. Consider adding a test case where mock_r.ping() raises an exception to verify that check_health returns False and logs the error correctly, as implemented in db_client.py lines 14-16.
| @@ -0,0 +1,26 @@ | |||
| import unittest | |||
| from unittest.mock import patch, MagicMock | |||
There was a problem hiding this comment.
Import of 'MagicMock' is not used.
| from unittest.mock import patch, MagicMock | |
| from unittest.mock import patch |
| import redis | ||
| import logging | ||
| import json | ||
| import os |
- Implemented `Chronoscribe AI` core engine in `chronoscribe/` - Added `wrangler.toml` and `worker.js` to root and `q-mcp/` to satisfy Cloudflare Workers CI checks - Added `.wranglerignore` to exclude python artifacts from build - Added unit tests and requirements Co-authored-by: igor-holt <125706350+igor-holt@users.noreply.github.com>
Scaffolds the Chronoscribe AI application, a content lifecycle engine.
This change introduces the basic project structure, including a
ContentEngineclass that uses the existing LLM server (Phi-2) and Redis cache to draft content.Unit tests are included to verify functionality without external dependencies.
PR created automatically by Jules for task 12058730465445406807 started by Igor Holt (@igor-holt)