Coverage for tests/test_free_models.py: 93.55%

50 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2026-09-27 04:08 +0000

1"""`free` separates "the provider charges nothing" from "nobody has entered a rate". 

2 

3Both were a ModelPrice with every field None, so they were the same bytes. The only hint was a 

4`:free` suffix in the model id, which is a naming convention rather than data. A consumer 

5holding a zero could not tell whether to report it as priced or to refuse it, and those want 

6opposite handling. Reported from a consuming metering system, which had a real Deepgram call 

7recorded as costing nothing. 

8 

9The flag drives two things, and both are pinned here: 

10 

11 1. `calc_price` reports an empty `unpriced_usage` for a free model, so `unpriced_usage` stays 

12 the single thing a caller reads to decide whether a zero is trustworthy. 

13 2. `exclude_free` keys on the flag rather than on "every rate is None", so the slim dataset 

14 drops genuinely free models and KEEPS unpriced ones. It previously dropped both, which 

15 made data.json and data_slim.json answer the same lookup differently. 

16""" 

17 

18from __future__ import annotations 

19 

20import json 

21from decimal import Decimal 

22from pathlib import Path 

23 

24import pytest 

25from pydantic import ValidationError 

26 

27from prices.prices_types import ClauseEquals, ModelInfo, ModelPrice as BuildModelPrice 

28from voice_prices import Usage, calc_price 

29 

30DATA = Path(__file__).parent.parent / 'prices' 

31 

32 

33def test_a_free_model_reports_nothing_unpriced(): 

34 """The zero is the answer, so there is no gap to report.""" 

35 calc = calc_price(Usage(input_tokens=1000), model_ref='moderation', provider_id='openai') 

36 assert calc.total_price == 0 

37 assert calc.model.free is True 

38 assert calc.unpriced_usage == () 

39 

40 

41def test_an_unpriced_model_still_reports_the_gap(): 

42 """Same zero, opposite meaning, and the caller can tell.""" 

43 calc = calc_price(Usage(audio_input_seconds=Decimal(60)), model_ref='nova-general', provider_id='deepgram') 

44 assert calc.total_price == 0 

45 assert calc.model.free is False 

46 assert calc.unpriced_usage == ('audio_input_seconds',) 

47 

48 

49@pytest.mark.parametrize( 

50 ('provider_id', 'model_ref', 'usage', 'free'), 

51 [ 

52 ('openai', 'moderation', Usage(input_tokens=1000), True), 

53 ('telnyx', 'us-tollfree-outbound', Usage(telephony_minutes=Decimal(5)), True), 

54 ('deepgram', 'nova-general', Usage(audio_input_seconds=Decimal(60)), False), 

55 ('deepgram', 'whisper-tiny', Usage(audio_input_seconds=Decimal(60)), False), 

56 ('elevenlabs', 'scribe_v1', Usage(audio_input_seconds=Decimal(60)), False), 

57 # Deprecated with "No rate was ever recorded here" in its own price_comments: the 

58 # clearest case of a zero that must not be billed on. 

59 ('cerebras', 'qwen-3-coder-480b', Usage(input_tokens=1000), False), 

60 ], 

61) 

62def test_every_zero_declares_which_kind_it_is(provider_id: str, model_ref: str, usage: Usage, free: bool): 

63 calc = calc_price(usage, model_ref=model_ref, provider_id=provider_id) 

64 assert calc.total_price == 0 

65 assert calc.model.free is free 

66 # The invariant a consumer can rely on: exactly one of the two signals fires. 

67 assert bool(calc.unpriced_usage) is (not free) 

68 

69 

70def test_free_and_a_rate_is_rejected(): 

71 """Contradictory claims must not be storable.""" 

72 with pytest.raises(ValidationError, match='free'): 

73 ModelInfo( 

74 id='x', 

75 match=ClauseEquals(equals='x'), 

76 free=True, 

77 prices=BuildModelPrice(input_mtok=Decimal(1)), 

78 ) 

79 

80 

81def test_free_with_an_empty_price_block_is_fine(): 

82 model = ModelInfo(id='x', match=ClauseEquals(equals='x'), free=True, prices=BuildModelPrice()) 

83 assert model.free is True 

84 

85 

86def _ids(path: Path, provider_id: str) -> set[str]: 

87 for provider in json.loads(path.read_text()): 87 ↛ 90line 87 didn't jump to line 90 because the loop on line 87 didn't complete

88 if provider['id'] == provider_id: 

89 return {m['id'] for m in provider['models']} 

90 return set() 

91 

92 

93def test_slim_drops_free_models_and_keeps_unpriced_ones(): 

94 """The bug this caused: the two datasets answered the same lookup differently. 

95 

96 `nova` returned a zero from data.json and raised LookupError from data_slim.json, because 

97 exclude_free could not tell an unpriced row from a free one. 

98 """ 

99 full, slim = DATA / 'data.json', DATA / 'data_slim.json' 

100 

101 # Free: dropped from slim, as intended. Slim is the priced subset. 

102 assert 'moderation' in _ids(full, 'openai') 

103 assert 'moderation' not in _ids(slim, 'openai') 

104 

105 # Unpriced: kept, so a lookup resolves the same way against either dataset. 

106 for provider_id, model_id in [('deepgram', 'nova'), ('deepgram', 'whisper'), ('elevenlabs', 'scribe_v1')]: 

107 assert model_id in _ids(full, provider_id) 

108 assert model_id in _ids(slim, provider_id), f'{provider_id}/{model_id} vanished from the slim dataset' 

109 

110 

111def test_the_free_suffix_convention_is_now_backed_by_the_field(): 

112 """`:free` in an id was the only signal. Anything carrying it must now say so in data.""" 

113 unmarked: list[str] = [] 

114 for provider in json.loads((DATA / 'data.json').read_text()): 

115 for model in provider['models']: 

116 if ':free' in model['id'] and not model.get('free'): 116 ↛ 117line 116 didn't jump to line 117 because the condition on line 116 was never true

117 unmarked.append(f'{provider["id"]}/{model["id"]}') 

118 assert not unmarked, f'models named `:free` but not flagged free: {unmarked[:5]}'