Skip to content

Add OpenApi code generation#2

Merged
code-schreiber merged 17 commits into
mainfrom
feat/openapi-integration
May 28, 2026
Merged

Add OpenApi code generation#2
code-schreiber merged 17 commits into
mainfrom
feat/openapi-integration

Conversation

@code-schreiber
Copy link
Copy Markdown
Owner

No description provided.

@code-schreiber code-schreiber marked this pull request as ready for review May 11, 2026 10:58
@opencode-agent
Copy link
Copy Markdown

PR Review: Add OpenAPI Code Generation

Overview

This PR adds an OpenAPI spec for the Hume EVI API, generates typed HTTP client code via swagger_parser + retrofit, replaces a manual HTTP GET in chat_provider.dart, and adds extensive tests (~468 lines).


🔴 Potential Bugs

  1. ErrorMessage has wrong type string (lib/evi_message.dart:43)

    ErrorMessage(json)
        : message = json['message'],
          super._('chat_metadata', json);  // ← should be 'error'

    This is a pre-existing bug — the ErrorMessage constructor passes 'chat_metadata' as the type instead of 'error'.

  2. New Dio instance created per API call (lib/provider/chat_provider.dart:75)

    final dio = Dio(BaseOptions(baseUrl: baseUrl));

    _fetchChatMessages creates a fresh Dio every invocation. Dio is designed to be a singleton — this loses connection pooling, interceptors, and adds allocation overhead. Extract it to a field or service locator.

  3. Cast safety in processEmotions (lib/provider/chat_provider.dart:119-124)
    The value ?? 0.0 is dead code since value is already checked for != null on line 120. More importantly, emotionFeatures.forEach assumes the decoded JSON is a Map — if the server returns a malformed JSON string that decodes to something else (e.g., a list), this will throw a runtime error. Use emotionFeatures is Map check before iterating.


🟡 Code Quality Issues

  1. OpenAPI security scheme type is wrong (lib/data/api/chat_events_spec.yaml:280-282)

    securitySchemes:
      bearerAuth:
        type: apiKey    # ← should be "http" with scheme: "bearer"
        in: header
        name: X-Hume-Api-Key

    Using type: apiKey for bearer auth is semantically incorrect per the OpenAPI spec. However, since the generated code passes the key as an explicit header parameter, this doesn't affect runtime behavior.

  2. Duplicate test names (test/provider/chat_provider_test.dart:442,454)
    Both tests are named 'ignores empty emotion_features'. The second test at line 454 actually tests an empty JSON object {}, not null — this is a different scenario and should have a distinct name.

  3. processEmotions iterates using forEach on dynamic — no type guard before iteration:

    final emotionFeatures = json.decode(message['emotion_features']);

    If emotionFeatures is not a Map, the forEach call crashes. A type check if (emotionFeatures is Map) would make this robust.

  4. Hardcoded OpenAI API key (lib/provider/chat_provider.dart:30)

    final String openAiApiKey = 'REPLACE_THIS_WITH_ACTUAL_OPEN_AI_API_KEY';

    Pre-existing, but worth noting: use ConfigManager like the Hume key, or at minimum an env var / .env file.

  5. _loadSavedChats has no retry logic — if SharedPreferences.getInstance() fails transiently, saved chats are silently lost until the next app restart.


🟢 What's Good

  • Excellent test coverage — model deserialization tests, filterMessages edge cases (null text, empty lists, mixed roles), processEmotions averaging, and EviMessage decoding with real prosody JSON.
  • $unknown fallback on all enums — this is a great pattern for backward compatibility when the API adds new enum values.
  • coverage:ignore-file on generated code — properly excludes auto-generated files from coverage metrics.
  • Swagger parser config (swagger_parser.yaml) is clean and minimal.
  • Generated Retrofit client properly handles null query params via removeWhere((k, v) => v == null).

💡 Suggestions

Priority Suggestion
High Make Dio a singleton (field in ChatProvider or a shared instance)
High Add is Map type guard before forEach in processEmotions
Medium Fix ErrorMessage type string (pre-existing bug worth fixing now)
Medium Rename duplicate test at line 454 to something like 'handles empty JSON emotion_features'
Medium Fix security scheme type in OpenAPI spec
Low Consider using indexWhere instead of while + removeAt(0) in filterMessages for O(n) instead of O(n²)
Low Use ConfigManager for OpenAI API key

github run

@opencode-agent
Copy link
Copy Markdown

PR Review: Add OpenAPI Code Generation

Overall: Solid PR with good patterns (generated code, $unknown enum fallback, excellent test coverage). A few bugs and improvements to address.


🔴 Bugs

1. ErrorMessage passes wrong type stringlib/evi_message.dart:43

super._('chat_metadata', json);  // should be 'error'

EviMessage.decode dispatches 'error'ErrorMessage, but the constructor stores 'chat_metadata' as the type. Anyone checking message.type on an error gets the wrong value. Fix now while you're touching this file.

2. New Dio instance per API calllib/provider/chat_provider.dart:75

final dio = Dio(BaseOptions(baseUrl: baseUrl));

_fetchChatMessages creates a fresh Dio every invocation, losing connection pooling and interceptors. Extract to a field or inject via constructor.

3. No type guard before .forEach on dynamicchat_provider.dart:119

final emotionFeatures = json.decode(message['emotion_features']);
if (emotionFeatures != null) {
  emotionFeatures.forEach((emotion, value) {  // crash if not Map

If the server sends emotion_features: "[]" or "\"string\"", json.decode returns a non-Map type and .forEach with a 2-arg callback throws at runtime. The try/catch masks this. Add if (emotionFeatures is Map) before iterating.

4. Dead code in processEmotionschat_provider.dart:120-122

if (emotion != null && value != null) {
  allEmotions[emotion] = (allEmotions[emotion] ?? 0.0) + (value ?? 0.0);
}

value ?? 0.0 is unreachable because value != null was just checked. Also emotion != null is always true for Map keys.

5. Empty test bodytest/data/api/models/return_chat_paged_events_test.dart:7

test('should parse return_chat_paged_events correctly', () {});  // no assertions

🟡 Code Quality

File Issue
chat_events_spec.yaml:280 type: apiKey for bearer auth is semantically wrong; should be type: http / scheme: bearer (no runtime impact since header is passed explicitly)
chat_provider.dart:160-237 _processWhatWentWell and _processChallenges are ~90% identical — extract shared HTTP call logic
chat_provider.dart:101-107 while + removeAt(0) is O(n²). Use indexWhere + sublist for O(n)
chat_provider.dart:30 openAiApiKey hardcoded as placeholder string — should use env var like the Hume key

🟢 What's Good

  • $unknown fallback on all generated enums — excellent pattern for backward compat when the API adds new values
  • coverage:ignore-file on generated code — correctly excludes auto-gen from coverage
  • Generated Retrofit client null-handlingremoveWhere((k, v) => v == null) on query params
  • Test coverage — ~468 lines covering model deserialization, filterMessages edge cases (null text, empty lists, mixed roles), processEmotions averaging and non-Map inputs, and EviMessage decoding with real prosody JSON
  • Clean swagger_parser.yaml — minimal, sensible config

💡 Priority Summary

Priority Action
High Fix ErrorMessage type string
High Extract Dio as a field/singleton
High Add is Map type guard in processEmotions
Medium Fix OpenAPI security scheme type
Medium Fill in empty test body
Low Refactor duplicate OpenAI prompt methods
Low O(n²) → O(n) in filterMessages

github run

@opencode-agent
Copy link
Copy Markdown

PR Review: Add OpenAPI Code Generation

Overall: Solid PR with meaningful improvements — replacing manual HTTP calls with a generated Retrofit client, adding an OpenAPI spec, and strong test coverage. A few real bugs and quality issues to fix.


🔴 Bugs

1. ErrorMessage passes wrong type stringlib/evi_message.dart:43

super._('chat_metadata', json);  // should be 'error'

EviMessage.decode dispatches 'error'ErrorMessage, but the constructor stores 'chat_metadata'. Anyone checking message.type gets wrong value.

2. New Dio instance per API calllib/provider/chat_provider.dart:75

final dio = Dio(BaseOptions(baseUrl: baseUrl));

Fresh Dio every _fetchChatMessages invocation — loses connection pooling, interceptors, and adds allocation overhead. Extract to a field.

3. No type guard before forEach on dynamicchat_provider.dart:119

emotionFeatures.forEach((emotion, value) { ... });

json.decode(message['emotion_features']) returns dynamic. If the server sends "[]" or "\"string\"", the 2-arg forEach throws at runtime. The try/catch masks this for production but the error is swallowed silently. Add if (emotionFeatures is Map) before iterating.

4. Empty test bodytest/data/api/models/return_chat_paged_events_test.dart:7

test('should parse return_chat_paged_events correctly', () {});  // no assertions

Evidently intended to have assertions; currently passes trivially.

5. Dead code in processEmotionschat_provider.dart:122

(value ?? 0.0)  // unreachable — value already checked != null on line 120

🟡 Code Quality

Issue File Detail
Security scheme type chat_events_spec.yaml:281 type: apiKey should be type: http / scheme: bearer (no runtime impact — header is passed explicitly)
Duplicate OpenAI call pattern chat_provider.dart:157-237 _processWhatWentWell / _processChallenges are ~90% identical — extract shared HTTP logic with a prompt parameter
O(n²) removal chat_provider.dart:101-107 while + removeAt(0) is O(n²). Use indexWhere + sublist for O(n)
Config not extracted chat_provider.dart:74 'https://api.hume.ai/' hardcoded — use ConfigManager or a constant

🟢 What's Good

  • $unknown fallback on all generated enums — best practice for backward compat when the API adds new enum values
  • coverage:ignore-file on generated code — correctly excludes auto-gen from coverage
  • Generated client null-handlingremoveWhere((k, v) => v == null) on query params
  • Test coverage — ~468 lines covering model deserialization, filterMessages edge cases (null text, empty lists, mixed roles), processEmotions averaging, and EviMessage decoding with real prosody JSON
  • Clean swagger_parser.yaml — minimal, sensible config

Priority Summary

Priority Action
High Fix ErrorMessage type string (chat_metadataerror)
High Extract Dio as a field/singleton in ChatProvider
High Add is Map type guard before forEach in processEmotions
Medium Fix OpenAPI security scheme type
Medium Fill in empty test body
Low Refactor duplicate OpenAI prompt methods into one
Low O(n²) → O(n) in filterMessages using indexWhere

github run

@code-schreiber code-schreiber merged commit 67aee06 into main May 28, 2026
3 checks passed
@code-schreiber code-schreiber deleted the feat/openapi-integration branch May 28, 2026 14:07
@code-schreiber
Copy link
Copy Markdown
Owner Author

The Expression Measurement API is being sunset, so I reverted all API related changes to avoid adding lots of soon-to-be dead code.
https://dev.hume.ai/docs/expression-measurement/overview

For clean Markdown of any page, append .md to the page URL.
For a complete documentation index, see https://dev.hume.ai/llms.txt.
For full documentation content, see https://dev.hume.ai/llms-full.txt.
For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://dev.hume.ai/_mcp/server.

Expression Measurement

The Expression Measurement API is being sunset.

  • May 14, 2026 - Last day to create new jobs through the Playground
  • June 14, 2026 - Last day to use the API and download job results

Hume's Expression Measurement API captures hundreds of dimensions of human expression from audio, video, images, and
text. Built on over a decade of research in computational emotion science,
these models go beyond basic sentiment to measure subtle expressions like admiration, awe, empathic pain, and
dozens more.

Expressions are complex and multifaceted. They should not be treated as direct inferences of emotional experience.
To learn more about the science behind expression measurement, visit the About the
science
page.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant