Skip to content

feat(config): improve config hot reload handling and introduce mock sse endpoint for ui testing as not all providers support prompt caching - #814

Merged
JayGhiya merged 1 commit into
mainfrom
query-changes-last-event
Sep 15, 2025
Merged

feat(config): improve config hot reload handling and introduce mock sse endpoint for ui testing as not all providers support prompt caching#814
JayGhiya merged 1 commit into
mainfrom
query-changes-last-event

Conversation

@JayGhiya

@JayGhiya JayGhiya commented Sep 15, 2025

Copy link
Copy Markdown
Member

User description

The changes in this commit improve the handling of configuration hot reloading in the config_hot_reload.py module. Specifically:

  • The EnvironmentSettings import is now wrapped in a conditional block to avoid circular imports.
  • The settings parameter in the _model_factory.build() call is now properly handled, falling back to creating a new EnvironmentSettings instance if settings is None.

These changes help ensure the configuration hot reload functionality works as expected and avoids potential issues with circular imports.

feat(experiments): add mock SSE server

This commit introduces a new standalone FastAPI application in the experiments directory that provides a mock Server-Sent Events (SSE) server. The purpose of this mock server is to replay events from a captured log file (docs/sse-agent-results.txt) to enable UI/development testing without running the full agent pipeline or external services.

The key features of the mock SSE server include:

  • Parsing the reference SSE log file and caching the events in memory on startup.
  • Exposing an API endpoint at /v1/codebase-agent-rules that streams the cached events to clients.
  • Allowing an optional delay between event emissions to simulate a real-time streaming experience.
  • Adding CORS middleware to enable cross-origin requests, matching the main application configuration.

This mock server provides a convenient way to test the UI and other components that consume the codebase agent rules SSE stream without the need for a fully running system.

fix(models): correct typo in app_interfaces field

This commit fixes a minor typo in the AgentMDOutput model, where the app_interaces field was misspelled as app_interaces. The correct field name is now app_interfaces.

This change ensures the model definition matches the expected field name and avoids potential issues with data serialization/deserialization.

chore(docker): add unoplat-code-confluence-query-engine service

This commit adds a new service definition for the unoplat-code-confluence-query-engine application in the local-docker-compose.yml file. The new service includes the following configuration:

  • Builds the application image using the Dockerfile in the unoplat-code-confluence-query-engine directory.
  • Sets the necessary environment variables for connecting to the PostgreSQL and Neo4j databases.
  • Exposes the application on port 8001.
  • Depends on the postgresql and neo4j services, ensuring they are healthy before starting the application.

This change makes it easier to run the full application stack locally using Docker Compose, including the query engine service.

refactor(codebase-agent-rules): improve SSE event naming

The changes in this commit improve the naming of the Server-Sent Events (SSE) emitted by the /v1/codebase-agent-rules endpoint. Specifically:

  • A new build_repo_agent_event_name() function is introduced to construct a namespaced event name in the format {repository_qualified_name}:{agent_name}:{event_type}.
  • This namespaced event name is used when emitting the initial "status" event and the final "aggregated_final_summary_agent" event.

Using the repository-qualified name as the first part of the event name ensures uniqueness across concurrent SSE connections for different repositories, improving the ability to track and identify events.


PR Type

Enhancement, Tests, Bug fix


Description

  • Add mock SSE server for UI testing without full pipeline

  • Improve SSE event naming with repository-qualified namespacing

  • Fix typo in app_interfaces field name

  • Add query engine service to Docker Compose configurations


Diagram Walkthrough

flowchart LR
  A["Mock SSE Server"] --> B["Parse SSE Log File"]
  B --> C["Stream Events to UI"]
  D["SSE Event Generator"] --> E["Build Namespaced Event Names"]
  E --> F["Repository:Agent:EventType Format"]
  G["Docker Compose"] --> H["Query Engine Service"]
Loading

File Walkthrough

Relevant files
Tests
2 files
mock_sse_server.py
Add standalone mock SSE server for testing                             
+161/-0 
yaak.rq_ZCT8QECJLF.yaml
Add HTTP request configuration for mock testing                   
+30/-0   
Enhancement
1 files
codebase_agent_rules.py
Implement namespaced SSE event naming system                         
+37/-6   
Bug fix
2 files
agent_md_output.py
Fix typo in app_interfaces field name                                       
+1/-1     
config_hot_reload.py
Improve import handling for circular dependency avoidance
+4/-1     
Configuration changes
3 files
local-docker-compose.yml
Add query engine service configuration                                     
+25/-0   
prod-docker-compose.yml
Add query engine service for production                                   
+22/-0   
Taskfile.yml
Add mock SSE server task command                                                 
+6/-0     
Additional files
1 files
__init__.py [link]   

…se endpoint for ui testing as not all providers support prompt caching

The changes in this commit improve the handling of configuration hot reloading in the `config_hot_reload.py` module. Specifically:

- The `EnvironmentSettings` import is now wrapped in a conditional block to avoid circular imports.
- The `settings` parameter in the `_model_factory.build()` call is now properly handled, falling back to creating a new `EnvironmentSettings` instance if `settings` is `None`.

These changes help ensure the configuration hot reload functionality works as expected and avoids potential issues with circular imports.

feat(experiments): add mock SSE server

This commit introduces a new standalone FastAPI application in the `experiments` directory that provides a mock Server-Sent Events (SSE) server. The purpose of this mock server is to replay events from a captured log file (`docs/sse-agent-results.txt`) to enable UI/development testing without running the full agent pipeline or external services.

The key features of the mock SSE server include:

- Parsing the reference SSE log file and caching the events in memory on startup.
- Exposing an API endpoint at `/v1/codebase-agent-rules` that streams the cached events to clients.
- Allowing an optional delay between event emissions to simulate a real-time streaming experience.
- Adding CORS middleware to enable cross-origin requests, matching the main application configuration.

This mock server provides a convenient way to test the UI and other components that consume the codebase agent rules SSE stream without the need for a fully running system.

fix(models): correct typo in app_interfaces field

This commit fixes a minor typo in the `AgentMDOutput` model, where the `app_interaces` field was misspelled as `app_interaces`. The correct field name is now `app_interfaces`.

This change ensures the model definition matches the expected field name and avoids potential issues with data serialization/deserialization.

chore(docker): add unoplat-code-confluence-query-engine service

This commit adds a new service definition for the `unoplat-code-confluence-query-engine` application in the `local-docker-compose.yml` file. The new service includes the following configuration:

- Builds the application image using the Dockerfile in the `unoplat-code-confluence-query-engine` directory.
- Sets the necessary environment variables for connecting to the PostgreSQL and Neo4j databases.
- Exposes the application on port 8001.
- Depends on the `postgresql` and `neo4j` services, ensuring they are healthy before starting the application.

This change makes it easier to run the full application stack locally using Docker Compose, including the query engine service.

refactor(codebase-agent-rules): improve SSE event naming

The changes in this commit improve the naming of the Server-Sent Events (SSE) emitted by the `/v1/codebase-agent-rules` endpoint. Specifically:

- A new `build_repo_agent_event_name()` function is introduced to construct a namespaced event name in the format `{repository_qualified_name}:{agent_name}:{event_type}`.
- This namespaced event name is used when emitting the initial "status" event and the final "aggregated_final_summary_agent" event.

Using the repository-qualified name as the first part of the event name ensures uniqueness across concurrent SSE connections for different repositories, improving the ability to track and identify events.
@claude

claude Bot commented Sep 15, 2025

Copy link
Copy Markdown

Claude encountered an error —— View job


I'll analyze this and get back to you.

@huly-for-github

Copy link
Copy Markdown

Connected to Huly®: UNOPL-951

@qodo-code-review

Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 Security concerns

CORS configuration:
Mock SSE server allows all origins, credentials, methods, and headers. Safe for local experiments but risky if deployed inadvertently; restrict origins for non-dev environments.

⚡ Recommended focus areas for review

Robustness

The SSE log parser assumes each event is on single-line fields and strips lines; multi-line data fields or leading spaces in the source log would be lost. Consider supporting multi-line "data:" fields and preserving exact content where needed.

def parse_sse_log_file(file_path: Path) -> List[Dict[str, str]]:
    """Parse a newline-delimited SSE capture file into event dicts.

    The expected format mirrors what `curl -N` would show:
      id: 1\n
      event: repo:agent:event_type\n
      data: {"json": "object"}\n
      \n
    - Blank line separates events
    - Lines starting with "::" are treated as comments/keepalive pings and skipped

    Returns a list of dicts each containing keys: "id", "event", "data".
    Data is preserved as a raw JSON string to match the real endpoint behavior.
    """
    events: List[Dict[str, str]] = []
    current: Dict[str, str] = {}

    try:
        raw_text: str = file_path.read_text(encoding="utf-8")
    except FileNotFoundError as exc:
        logger.error("SSE log file not found at {}", file_path)
        raise exc

    for raw_line in raw_text.splitlines():
        line: str = raw_line.strip()

        if not line:
            if "event" in current and "data" in current:
                if "id" not in current:
                    current["id"] = str(len(events))
                events.append(current)
            current = {}
            continue

        if line.startswith("::"):
            # Skip comment/ping lines
            continue

        if line.startswith("id:"):
            current["id"] = line.partition("id:")[2].strip()
            continue

        if line.startswith("event:"):
            current["event"] = line.partition("event:")[2].strip()
            continue

        if line.startswith("data:"):
            # Preserve raw JSON string as-is
            current["data"] = line.partition("data:")[2].strip()
            continue

    # Finalize trailing event if file doesn't end with a blank line
    if current and "event" in current and "data" in current:
        if "id" not in current:
            current["id"] = str(len(events))
        events.append(current)

    return events
Compatibility

Event names changed to namespaced values; ensure all SSE consumers are updated accordingly and that filtering/handlers expect the new "repo:agent:event_type" format to avoid breaking the UI.

def build_repo_agent_event_name(
    repository_qualified_name: str, agent_name: str, event_type: str
) -> str:
    """Construct a namespaced SSE event string as repo:agent:event_type.

    Using repository-qualified name first ensures uniqueness across concurrent
    SSE connections for different repositories.
    """
    return f"{repository_qualified_name}:{agent_name}:{event_type}"
Networking

The new service exposes port 8001:8000 while your Taskfile dev runs on 8001; verify no port conflicts and that dependent services reach the correct internal hostname and port.

unoplat-code-confluence-query-engine:
  # container_name: unoplat-code-confluence-query-engine
  build:
    context: ./unoplat-code-confluence-query-engine
    dockerfile: Dockerfile
  environment:
    - NEO4J_HOST=neo4j
    - NEO4J_PORT=7687
    - NEO4J_USERNAME=neo4j
    - NEO4J_PASSWORD=password
    - DB_HOST=postgresql
    - DB_PORT=5432
    - DB_USER=postgres
    - DB_PASSWORD=postgres
    - DB_NAME=code_confluence
  ports:
    - "8001:8000"
  networks:
    - temporal-network
  depends_on:
    postgresql:
      condition: service_healthy
    neo4j:
      condition: service_healthy

@qodo-code-review

Copy link
Copy Markdown

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: claude-review

Failed stage: Run Claude Code Review [❌]

Failure summary:

The action failed because the Claude run hit a usage limit and returned an error:
- The model
response contained the message 5-hour limit reached ∙ resets 3pm, indicating a rate/usage window
limit was reached.
- The run result was marked with "subtype": "success", "is_error": true, leading
the workflow to exit with code 1.
- Environment shows CLAUDE_SUCCESS: false, confirming the Claude
step failed.
- Additionally, copying slash commands failed with Slash commands directory not found
or error copying: ShellError: Failed with exit code 1, but the primary failure was the usage limit
error.

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

549:  ANTHROPIC_BEDROCK_BASE_URL: 
550:  ANTHROPIC_VERTEX_PROJECT_ID: 
551:  CLOUD_ML_REGION: 
552:  GOOGLE_APPLICATION_CREDENTIALS: 
553:  ANTHROPIC_VERTEX_BASE_URL: 
554:  VERTEX_REGION_CLAUDE_3_5_HAIKU: 
555:  VERTEX_REGION_CLAUDE_3_5_SONNET: 
556:  VERTEX_REGION_CLAUDE_3_7_SONNET: 
557:  ##[endgroup]
558:  Setting up Claude settings at: /home/runner/.claude/settings.json
559:  Creating .claude directory...
560:  No existing settings file found, creating new one
561:  Updated settings with enableAllProjectMcpServers: true
562:  Settings saved successfully
563:  Copying slash commands from /home/runner/work/_actions/anthropics/claude-code-action/beta/slash-commands to /home/runner/.claude/
564:  Slash commands directory not found or error copying: ShellError: Failed with exit code 1
565:  Prompt file size: 17016 bytes
...

657:  },
658:  "content": [
659:  {
660:  "type": "text",
661:  "text": "5-hour limit reached ∙ resets 3pm"
662:  }
663:  ]
664:  },
665:  "parent_tool_use_id": null,
666:  "session_id": "c3c065ed-8805-4634-ae06-d6840862c099",
667:  "uuid": "da349fb2-7b8c-47a1-9e2c-24ea2704da92"
668:  }
669:  {
670:  "type": "result",
671:  "subtype": "success",
672:  "is_error": true,
673:  "duration_ms": 397,
...

677:  "session_id": "c3c065ed-8805-4634-ae06-d6840862c099",
678:  "total_cost_usd": 0,
679:  "usage": {
680:  "input_tokens": 0,
681:  "cache_creation_input_tokens": 0,
682:  "cache_read_input_tokens": 0,
683:  "output_tokens": 0,
684:  "server_tool_use": {
685:  "web_search_requests": 0
686:  },
687:  "service_tier": "standard"
688:  },
689:  "permission_denials": [],
690:  "uuid": "57a0a22e-86ba-432a-8223-1c1d87cf0354"
691:  }
692:  ##[error]Process completed with exit code 1.
693:  ##[group]Run bun run ${GITHUB_ACTION_PATH}/src/entrypoints/update-comment-link.ts
...

698:  DISALLOWED_TOOLS: WebSearch,WebFetch
699:  REPOSITORY: unoplat/unoplat-code-confluence
700:  PR_NUMBER: 814
701:  CLAUDE_COMMENT_ID: 3292186639
702:  GITHUB_RUN_ID: 17734846014
703:  GITHUB_TOKEN: ***
704:  GITHUB_EVENT_NAME: pull_request
705:  TRIGGER_COMMENT_ID: 
706:  CLAUDE_BRANCH: 
707:  IS_PR: false
708:  BASE_BRANCH: 
709:  CLAUDE_SUCCESS: false
710:  OUTPUT_FILE: /home/runner/work/_temp/claude-execution-output.json
711:  TRIGGER_USERNAME: JayGhiya
712:  PREPARE_SUCCESS: true
713:  PREPARE_ERROR: 
714:  USE_STICKY_COMMENT: false

@qodo-code-review

Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Integrate the mock server into the main application

Merge the new standalone mock SSE server into the main application, activating
it with an environment variable or query parameter. This change would simplify
the development setup and help keep the mock and real APIs synchronized.

Examples:

unoplat-code-confluence-query-engine/experiments/mock_sse_server.py [1-161]
"""Mock SSE server that replays events from docs/sse-agent-results.txt.

This standalone FastAPI app exposes an endpoint compatible with the real
`/v1/codebase-agent-rules` route and streams Server-Sent Events in the exact
order captured in the reference log file. Useful for UI/dev testing without
running the full agent pipeline or external services.
"""

from __future__ import annotations


 ... (clipped 151 lines)
unoplat-code-confluence-query-engine/Taskfile.yml [57-61]
  mock-sse:
    desc: Run mock SSE server (replays docs/sse-agent-results.txt) on port 8002
    dir: experiments
    cmds:
      - uv run fastapi dev mock_sse_server.py --port 8002

Solution Walkthrough:

Before:

# file: experiments/mock_sse_server.py
# A new, separate FastAPI application is created for mocking.
app: FastAPI = FastAPI(title="Mock ... SSE Server")

@app.on_event("startup")
async def on_startup() -> None:
    # Loads events from a file
    ...

@app.get("/v1/codebase-agent-rules")
async def get_mock_codebase_agent_rules(...) -> EventSourceResponse:
    # Streams the pre-loaded mock events
    return EventSourceResponse(...)

# file: Taskfile.yml
# A separate command is needed to run this mock server.
# mock-sse:
#   cmds:
#     - uv run fastapi dev mock_sse_server.py --port 8002

After:

# file: src/.../main.py (conceptual)
# The main application is modified to include the mock logic.
app: FastAPI = FastAPI(...)

# A configuration setting controls the mock behavior.
USE_MOCK_SSE = os.getenv("USE_MOCK_SSE", "false").lower() == "true"

@app.get("/v1/codebase-agent-rules")
async def get_codebase_agent_rules(
    request: Request,
    # ... other params
) -> EventSourceResponse:
    if USE_MOCK_SSE:
        # Use the same logic from the mock server, but integrated.
        return EventSourceResponse(stream_mock_events(request, ...))
    else:
        # The original logic for generating real events.
        return EventSourceResponse(generate_sse_events(...))
Suggestion importance[1-10]: 7

__

Why: The suggestion proposes a valid architectural improvement by integrating the mock server into the main application, which would simplify the development workflow and reduce maintenance overhead.

Medium
Possible issue
Fix incorrect parsing of multi-line SSE data

Modify the SSE parser to correctly handle multi-line data fields by appending
subsequent data lines with a newline, instead of overwriting the field.

unoplat-code-confluence-query-engine/experiments/mock_sse_server.py [78-81]

 if line.startswith("data:"):
     # Preserve raw JSON string as-is
-    current["data"] = line.partition("data:")[2].strip()
+    new_data = line.partition("data:")[2].strip()
+    if "data" in current:
+        current["data"] += "\n" + new_data
+    else:
+        current["data"] = new_data
     continue
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies a bug in the new SSE parser where it fails to handle multi-line data fields, improving the mock server's robustness and compliance with the SSE standard.

Low
General
Avoid blocking event loop on startup

Make the on_startup function synchronous by removing async def to avoid blocking
the event loop with synchronous file I/O.

unoplat-code-confluence-query-engine/experiments/mock_sse_server.py [125-130]

 @app.on_event("startup")
-async def on_startup() -> None:
+def on_startup() -> None:
     """Load and cache SSE events from the reference file on startup."""
     global LOADED_EVENTS
     LOADED_EVENTS = parse_sse_log_file(SSE_LOG_PATH)
     logger.info("Loaded {} mock SSE events from {}", len(LOADED_EVENTS), SSE_LOG_PATH)
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why: The suggestion correctly identifies that a synchronous, blocking I/O call is made within an async function, which is an anti-pattern. The proposed fix to make the function synchronous is appropriate and improves code quality.

Low
  • More

@JayGhiya
JayGhiya merged commit d2d8dcb into main Sep 15, 2025
5 of 6 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex Review: Here are some suggestions.

Reply with @codex fix comments to fix any unresolved comments.

About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you open a pull request for review, mark a draft as ready, or comment "@codex review". If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex fix this CI failure" or "@codex address that feedback".

Comment on lines +23 to +27
# Resolve repository root and reference SSE log file
REPO_ROOT: Path = Path(__file__).resolve().parents[1]
SSE_LOG_PATH: Path = REPO_ROOT / "docs" / "sse-agent-results.txt"


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Handle missing SSE log file in mock server

The mock SSE app loads REPO_ROOT / "docs" / "sse-agent-results.txt" during startup, but no such file exists anywhere in the repository (see find . -name "sse-agent-results.txt" returning nothing). Because parse_sse_log_file immediately raises FileNotFoundError when the file is missing, the FastAPI application never finishes starting and the new task mock-sse or Docker usage will crash before serving any events. Either ship the reference file with the repo or guard the startup logic so the server can run without it.

Useful? React with 👍 / 👎.

JayGhiya added a commit that referenced this pull request Apr 8, 2026
feat(config): improve config hot reload handling and introduce mock sse endpoint for ui testing as not all providers support prompt caching
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant