Repository: promptfoo/promptfoo
Issue: `named_score` returned from python assertion not being shown in the UI
Root cause: Interface Contract Mismatch

**Describe the bug**

When defining assertions in a python file, the documentation states:
```
@dataclass
class GradingResult:
    pass_: bool  # 'pass' is a reserved keyword in Python
    score: float
    reason: str
    component_results: Optional[List['GradingResult']] = None
    named_scores: Optional[Dict[str, float]] = None  # Appear as metrics in the UI
```

However, returning one such object does not have the metrics shown in the UI

**To Reproduce**

I've created this small github repo with a simple example that will replicate the error: https://github.com/donatoaz/promptfoo-named-scores-issue

It has a screenshot of the issue

**Expected behavior**

I expected those metrics returned as `named_scores` to be treated the same as those defined via the `metrics` key in a yaml test/assertion file

**Screenshots**

![Image](https://github.com/user-attachments/assets/e586a3db-fa9e-41de-b6c1-6d9ce94664eb)

**System information:**

If possible, please output the results of `promptfoo debug` and paste the output here.

```

========================================================================================================================
⚠️ The current version of promptfoo 0.114.5 is lower than the latest available version 0.114.7.

Please run npx promptfoo@latest or npm install -g promptfoo@latest to update.
========================================================================================================================

========================================================================================================================
Promptfoo Debug Information
========================================================================================================================
{
  "version": "0.114.5",
  "platform": {
    "os": "darwin",
    "release": "24.3.0",
    "arch": "arm64",
    "nodeVersion": "v20.17.0"
  },
  "env": {},
  "configInfo": {
    "defaultConfigPath": "/Users/donatoaz/src/aws/tooling/contrib/replicate_named_score_issue/promptfooconfig.yaml",
    "configExists": true,
    "configContent": {
      "config": {
        "tags": {},
        "description": "Sample evaluation",
        "prompts": [
          {
            "id": "file:///Users/donatoaz/src/aws/tooling/contrib/replicate_named_score_issue/prompts/amazon_nova_prompt.json",
            "label": "amazon_nova"
          }
        ],
        "providers": [
          {
            "id": "bedrock:us.amazon.nova-lite-v1:0",
            "label": "Amazon Nova lite",
            "prompts": [
              "amazon_nova"
            ]
          }
        ],
        "tests": [
          "file://tests/my_test.yaml"
        ],
        "scenarios": [],
        "env": {},
        "sharing": true,
        "defaultTest": {
          "vars": {},
          "assert": [],
          "options": {},
          "metadata": {}
        },
        "outputPath": [],
        "extensions": [],
        "metadata": {}
      },
      "testSuite": {
        "description": "Sample evaluation",
        "tags": {},
        "prompts": [
          {
            "raw": "[\n  {\n    \"role\": \"system\",\n    \"content\": \"You are a stock analyst. You analyze shareholder letters and respond to user questions.\"\n  },\n  {\n    \"role\": \"user\",\n    \"content\": [\n      {\n        \"text\": \"Document: {{pdf_document}}\\n\\nWhat was the revenue growth? Give me just the number in percentage.\"\n      }\n    ]\n  }\n]\n",
            "label": "amazon_nova"
          }
        ],
        "providers": [
          {
            "env": {},
            "modelName": "us.amazon.nova-lite-v1:0",
            "config": {
              "basePath": "/Users/donatoaz/src/aws/tooling/contrib/replicate_named_score_issue"
            },
            "label": "Amazon Nova lite"
          }
        ],
        "providerPromptMap": {
          "bedrock:us.amazon.nova-lite-v1:0": [
            "amazon_nova"
          ],
          "Amazon Nova lite": [
            "amazon_nova"
          ]
        },
        "tests": [
          {
            "vars": {
              "pdf_document": "file://tests/docs/Shareholderletter97.pdf"
            },
            "description": "This will not show metrics",
            "assert": [
              {
                "type": "python",
                "value": "file://tests/my_assert.py:get_assert"
              }
            ]
          },
          {
            "vars": {
              "pdf_document": "file://tests/docs/Shareholderletter97.pdf"
            },
            "description": "This will show metrics",
            "assert": [
              {
                "type": "icontains",
                "value": "838%",
                "metric": "foo_metric"
              }
            ]
          }
        ],
        "scenarios": [],
        "defaultTest": {
          "metadata": {},
          "options": {},
          "vars": {},
          "assert": []
        },
        "nunjucksFilters": {},
        "extensions": []
      },
      "basePath": "/Users/donatoaz/src/aws/tooling/contrib/replicate_named_score_issue"
    }
  }
}
========================================================================================================================
Please include this output when reporting issues on GitHub: https://github.com/promptfoo/promptfoo/issues
```

- Promptfoo version: 0.114.5

**Additional context**
Add any other context about the problem here.


This issue was fixed in PR #4398 and released in **v0.115.0**.

The fix added the `mapSnakeCaseToCamelCase` function in `src/assertions/python.ts` which properly converts Python's `named_scores` (snake_case) to JavaScript's `namedScores` (camelCase), ensuring metrics from Python assertions appear correctly in the UI.

**Solution**: Upgrade to v0.115.0 or later:
```bash
npm install -g promptfoo@latest
# or
npx promptfoo@latest eval
```

Your `named_scores` from Python assertions will now display as metrics in the UI.

Closing as this was already fixed in v0.115.0 (PR #4398).