*** Settings ***
Documentation    Integration tests for LLM system prompt template rendering with runtime context
Library          OperatingSystem
Library          String
Library          Collections
Library          Process
Resource    ${CURDIR}/v2_paths.resource

*** Variables ***
${TEMP_DIR}      /tmp/cleveragents_test
${TEST_CONTEXT}  system_prompt_template_test

*** Test Cases ***
System Prompt Template Variables Are Rendered With Runtime Context
    [Documentation]    Verify that JINJA2 templates in system prompts are rendered with actual runtime context
    [Tags]    integration    llm    template    system_prompt

    # Create a simple config with templated system prompt
    ${config_content}=    Catenate    SEPARATOR=
    ...    cleveragents:\n
    ...    ${SPACE}${SPACE}version: "3.0"\n
    ...    ${SPACE}${SPACE}template_engine: "JINJA2"\n
    ...

    ...    actors:\n
    ...    ${SPACE}${SPACE}filter_actor:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}type: llm\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}config:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}provider: openai\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}model: gpt-3.5-turbo\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}system_prompt: "Topic: {{ context.topic | tojson }}, List: {{ context.items | length }}"\n
    ...

    ...    routes:\n
    ...    ${SPACE}${SPACE}main:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}type: stream\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}publications:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}- __output__\n
    ...    \n
    ...    merges:\n
    ...    ${SPACE}${SPACE}- sources: [__input__]\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}target: main\n

    Create File    ${TEMP_DIR}/template_test.yaml    ${config_content}

    # Create context with test data
    ${context_data}=    Set Variable    {"topic": "Artificial Intelligence", "audience": "researchers"}
    Create File    ${TEMP_DIR}/test_context.json    ${context_data}

    # Run the agent with --load-context - the LLM should receive a system prompt with rendered values
    # Since we can't easily intercept the actual LLM call, we verify the config was loaded correctly
    ${result}=    Run Process    ${PYTHON}    -m    cleveragents    actor    run    -c    ${TEMP_DIR}/template_test.yaml    --unsafe    --allow-rxpy-in-run-mode    --load-context    ${TEMP_DIR}/test_context.json    test-actor    "Analyze this"
    ...    shell=False    timeout=120s    on_timeout=kill

    # The agent should have processed successfully (even if it times out, the config should load)
    Should Not Contain    ${result.stderr}    TemplateError
    Should Not Contain    ${result.stderr}    Failed to render
    Remove File    ${TEMP_DIR}/template_test.yaml
    Remove File    ${TEMP_DIR}/test_context.json

Config Parser Preserves Template Syntax During YAML Loading
    [Documentation]    Verify that template markers are not prematurely rendered during config parsing
    [Tags]    integration    config    template

    # Create config with templates
    ${config_content}=    Catenate    SEPARATOR=
    ...    cleveragents:\n
    ...    ${SPACE}${SPACE}version: "3.0"\n
    ...    ${SPACE}${SPACE}template_engine: "JINJA2"\n
    ...    \n
    ...    actors:\n
    ...    ${SPACE}${SPACE}actor1:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}type: llm\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}config:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}provider: openai\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}model: gpt-3.5-turbo\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}system_prompt: "Topic: {{ context.topic }}"\n
    ...    ${SPACE}${SPACE}actor2:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}type: llm\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}config:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}provider: openai\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}model: gpt-3.5-turbo\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}system_prompt: "Data: {{ context.data }}"\n
    ...    \n
    ...    routes:\n
    ...    ${SPACE}${SPACE}main:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}type: stream\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}publications:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}- __output__\n

    Create File    ${TEMP_DIR}/preserve_test.yaml    ${config_content}

    # The config should load without errors and preserve templates
    ${python_code}=    Set Variable    from pathlib import Path; from cleveragents.reactive.config_parser import ReactiveConfigParser; parser = ReactiveConfigParser(); config = parser.parse_files([Path('${TEMP_DIR}/preserve_test.yaml')]); agent1_prompt = config.agents['actor1'].config.get('system_prompt', ''); print('PROMPT1:', agent1_prompt); assert '{{' in agent1_prompt and '}}' in agent1_prompt, f'Templates not preserved: {agent1_prompt}'
    ${result}=    Run Process    ${PYTHON}    -c    ${python_code}    shell=False    timeout=120s    on_timeout=kill

    Should Be Equal As Integers    ${result.rc}    0
    Should Contain    ${result.stdout}    PROMPT1:
    Should Contain    ${result.stdout}    {{ context.topic }}

    # Clean up
    Remove File    ${TEMP_DIR}/preserve_test.yaml

Nested Context Variables In System Prompts
    [Documentation]    Verify that nested context variables like context.paper_details.topic work correctly
    [Tags]    integration    llm    template    nested

    # Create config with nested template variables
    ${config_content}=    Catenate    SEPARATOR=
    ...    cleveragents:\n
    ...    ${SPACE}${SPACE}version: "3.0"\n
    ...    ${SPACE}${SPACE}template_engine: "JINJA2"\n
    ...    ${SPACE}${SPACE}unsafe: true\n
    ...    \n
    ...    actors:\n
    ...    ${SPACE}${SPACE}nested_actor:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}type: llm\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}config:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}provider: openai\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}model: gpt-3.5-turbo\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}system_prompt: |\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}Paper Details:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}- Topic: {{ context.paper_details.topic }}\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}- Length: {{ context.paper_details.length }}\n
    ...    \n
    ...    routes:\n
    ...    ${SPACE}${SPACE}main:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}type: stream\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}operators:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}- type: map\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}params:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}actor: nested_actor\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}publications:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}- __output__\n
    ...    \n
    ...    merges:\n
    ...    ${SPACE}${SPACE}- sources: [__input__]\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}target: main\n

    Create File    ${TEMP_DIR}/nested_test.yaml    ${config_content}

    # Create nested context
    ${context_data}=    Set Variable    {"paper_details": {"topic": "Machine Learning", "length": "5000 words"}}
    Create File    ${TEMP_DIR}/nested_context.json    ${context_data}

    # Verify the config loads and preserves templates
    ${python_code}=    Set Variable    from pathlib import Path; from cleveragents.reactive.config_parser import ReactiveConfigParser; parser = ReactiveConfigParser(); config = parser.parse_files([Path('${TEMP_DIR}/nested_test.yaml')]); prompt = config.agents['nested_actor'].config.get('system_prompt', ''); print('NESTED_PROMPT:', prompt); assert '{{ context.paper_details.topic }}' in prompt, f'Nested template not preserved: {prompt}'
    ${result}=    Run Process    ${PYTHON}    -c    ${python_code}    shell=False    timeout=120s    on_timeout=kill

    Should Be Equal As Integers    ${result.rc}    0
    Should Contain    ${result.stdout}    {{ context.paper_details.topic }}

    # Clean up
    Remove File    ${TEMP_DIR}/nested_test.yaml
    Remove File    ${TEMP_DIR}/nested_context.json

JINJA2 Filters In System Prompts Are Preserved
    [Documentation]    Verify that JINJA2 filters like tojson are preserved during config loading
    [Tags]    integration    template    jinja2    filters

    # Create config with JINJA2 filters
    ${config_content}=    Catenate    SEPARATOR=
    ...    cleveragents:\n
    ...    ${SPACE}${SPACE}version: "3.0"\n
    ...    ${SPACE}${SPACE}template_engine: "JINJA2"\n
    ...    \n
    ...    actors:\n
    ...    ${SPACE}${SPACE}filter_actor:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}type: llm\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}config:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}provider: openai\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}model: gpt-3.5-turbo\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}system_prompt: "Topic: {{ context.topic | tojson }}, List: {{ context.items | length }}"\n
    ...    \n
    ...    routes:\n
    ...    ${SPACE}${SPACE}main:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}type: stream\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}publications:\n
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}${SPACE}- __output__\n

    Create File    ${TEMP_DIR}/filter_test.yaml    ${config_content}

    # Verify filters are preserved
    ${python_code}=    Set Variable    from pathlib import Path; from cleveragents.reactive.config_parser import ReactiveConfigParser; parser = ReactiveConfigParser(); config = parser.parse_files([Path('${TEMP_DIR}/filter_test.yaml')]); prompt = config.agents['filter_actor'].config.get('system_prompt', ''); print('FILTER_PROMPT:', prompt); assert '| tojson' in prompt and '| length' in prompt, f'Filters not preserved: {prompt}'
    ${result}=    Run Process    ${PYTHON}    -c    ${python_code}    shell=False    timeout=120s    on_timeout=kill

    Should Be Equal As Integers    ${result.rc}    0
    Should Contain    ${result.stdout}    | tojson
    Should Contain    ${result.stdout}    | length

    # Clean up
    Remove File    ${TEMP_DIR}/filter_test.yaml

*** Keywords ***
Suite Setup
    Create Directory    ${TEMP_DIR}

Suite Teardown
    Remove Directory    ${TEMP_DIR}    recursive=True
