Skip to content

Commit 1d77d3e

Browse files
authored
Merge pull request #243 from codelion/fix-token-usage
Fix token usage
2 parents d1baf93 + a168667 commit 1d77d3e

File tree

6 files changed

+108
-8
lines changed

6 files changed

+108
-8
lines changed

optillm/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Version information
2-
__version__ = "0.2.4"
2+
__version__ = "0.2.5"
33

44
# Import from server module
55
from .server import (

optillm/plugins/proxy_plugin.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ def run(system_prompt: str, initial_query: str, client, model: str,
5454
{"role": "user", "content": initial_query}
5555
]
5656
)
57-
return response.choices[0].message.content, response.usage.completion_tokens
57+
# Return full response dict to preserve all usage information
58+
response_dict = response.model_dump() if hasattr(response, 'model_dump') else response
59+
return response_dict, 0
5860

5961
# Create or reuse proxy client to maintain state (important for round-robin)
6062
config_key = str(config) # Simple config-based cache key
@@ -128,7 +130,9 @@ def run(system_prompt: str, initial_query: str, client, model: str,
128130
**(request_config or {})
129131
)
130132

131-
return response.choices[0].message.content, response.usage.completion_tokens
133+
# Return full response dict to preserve all usage information
134+
response_dict = response.model_dump() if hasattr(response, 'model_dump') else response
135+
return response_dict, 0
132136

133137
except Exception as e:
134138
logger.error(f"Proxy plugin error: {e}", exc_info=True)
@@ -141,4 +145,6 @@ def run(system_prompt: str, initial_query: str, client, model: str,
141145
{"role": "user", "content": initial_query}
142146
]
143147
)
144-
return response.choices[0].message.content, response.usage.completion_tokens
148+
# Return full response dict to preserve all usage information
149+
response_dict = response.model_dump() if hasattr(response, 'model_dump') else response
150+
return response_dict, 0

optillm/server.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,22 @@ def proxy():
788788

789789
# Handle non-none approaches with n attempts
790790
response, completion_tokens = execute_n_times(n, approaches, operation, system_prompt, initial_query, client, model, request_config, request_id)
791+
792+
# Check if the response is a full dict (like from proxy plugin or none approach)
793+
if operation == 'SINGLE' and isinstance(response, dict) and 'choices' in response and 'usage' in response:
794+
# This is a full response dict, return it directly
795+
if conversation_logger and request_id:
796+
conversation_logger.log_final_response(request_id, response)
797+
conversation_logger.finalize_conversation(request_id)
798+
799+
if stream:
800+
if request_id:
801+
logger.info(f'Request {request_id}: Completed (streaming response)')
802+
return Response(generate_streaming_response(extract_contents(response), model), content_type='text/event-stream')
803+
else:
804+
if request_id:
805+
logger.info(f'Request {request_id}: Completed')
806+
return jsonify(response), 200
791807

792808
except Exception as e:
793809
# Log error to conversation logger if enabled

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "optillm"
7-
version = "0.2.4"
7+
version = "0.2.5"
88
description = "An optimizing inference proxy for LLMs."
99
readme = "README.md"
1010
license = "Apache-2.0"

tests/test_ci_quick.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from optillm.plugins.deep_research import DeepResearcher
4545
from optillm.plugins.longcepo import run_longcepo
4646
from optillm.plugins.spl import run_spl
47+
from optillm.plugins.proxy import client, config, approach_handler
4748
print("✅ Plugin submodule imports working - no relative import errors")
4849
except ImportError as e:
4950
if "attempted relative import" in str(e):

tests/test_plugins.py

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ def test_plugin_module_imports():
3030
'optillm.plugins.deep_research_plugin',
3131
'optillm.plugins.deepthink_plugin',
3232
'optillm.plugins.longcepo_plugin',
33-
'optillm.plugins.spl_plugin'
33+
'optillm.plugins.spl_plugin',
34+
'optillm.plugins.proxy_plugin'
3435
]
3536

3637
for module_name in plugin_modules:
@@ -51,7 +52,7 @@ def test_plugin_approach_detection():
5152
load_plugins()
5253

5354
# Check if known plugins are loaded
54-
expected_plugins = ["memory", "readurls", "privacy", "web_search", "deep_research", "deepthink", "longcepo", "spl"]
55+
expected_plugins = ["memory", "readurls", "privacy", "web_search", "deep_research", "deepthink", "longcepo", "spl", "proxy"]
5556
for plugin_name in expected_plugins:
5657
assert plugin_name in plugin_approaches, f"Plugin {plugin_name} not loaded"
5758

@@ -141,6 +142,63 @@ def test_spl_plugin():
141142
assert run_spl is not None
142143

143144

145+
def test_proxy_plugin():
146+
"""Test proxy plugin module"""
147+
import optillm.plugins.proxy_plugin as plugin
148+
assert hasattr(plugin, 'run')
149+
assert hasattr(plugin, 'SLUG')
150+
assert plugin.SLUG == "proxy"
151+
152+
# Test proxy submodules can be imported
153+
from optillm.plugins.proxy import client, config, approach_handler
154+
assert client is not None
155+
assert config is not None
156+
assert approach_handler is not None
157+
158+
159+
def test_proxy_plugin_token_counts():
160+
"""Test that proxy plugin returns complete token usage information"""
161+
import optillm.plugins.proxy_plugin as plugin
162+
from unittest.mock import Mock, MagicMock
163+
164+
# Create a mock client with a mock response that has all token counts
165+
mock_client = Mock()
166+
mock_response = MagicMock()
167+
mock_response.choices = [Mock(message=Mock(content="Test response"))]
168+
mock_response.usage = Mock(
169+
prompt_tokens=10,
170+
completion_tokens=5,
171+
total_tokens=15
172+
)
173+
mock_response.model_dump.return_value = {
174+
'choices': [{'message': {'content': 'Test response'}}],
175+
'usage': {
176+
'prompt_tokens': 10,
177+
'completion_tokens': 5,
178+
'total_tokens': 15
179+
}
180+
}
181+
mock_client.chat.completions.create.return_value = mock_response
182+
183+
# Run the proxy plugin
184+
result, _ = plugin.run(
185+
system_prompt="Test system",
186+
initial_query="Test query",
187+
client=mock_client,
188+
model="test-model"
189+
)
190+
191+
# Verify the result contains all token counts
192+
assert isinstance(result, dict), "Result should be a dictionary"
193+
assert 'usage' in result, "Result should contain usage information"
194+
assert 'prompt_tokens' in result['usage'], "Usage should contain prompt_tokens"
195+
assert 'completion_tokens' in result['usage'], "Usage should contain completion_tokens"
196+
assert 'total_tokens' in result['usage'], "Usage should contain total_tokens"
197+
assert result['usage']['prompt_tokens'] == 10
198+
assert result['usage']['completion_tokens'] == 5
199+
assert result['usage']['total_tokens'] == 15
200+
201+
144202
def test_plugin_subdirectory_imports():
145203
"""Test all plugins with subdirectories can import their submodules"""
146204
# Test deep_research
@@ -159,6 +217,12 @@ def test_plugin_subdirectory_imports():
159217
# Test spl
160218
from optillm.plugins.spl import run_spl
161219
assert run_spl is not None
220+
221+
# Test proxy
222+
from optillm.plugins.proxy import client, config, approach_handler
223+
assert client is not None
224+
assert config is not None
225+
assert approach_handler is not None
162226

163227

164228
def test_no_relative_import_errors():
@@ -170,7 +234,8 @@ def test_no_relative_import_errors():
170234
'optillm.plugins.deepthink_plugin',
171235
'optillm.plugins.deep_research_plugin',
172236
'optillm.plugins.longcepo_plugin',
173-
'optillm.plugins.spl_plugin'
237+
'optillm.plugins.spl_plugin',
238+
'optillm.plugins.proxy_plugin'
174239
]
175240

176241
for plugin_name in plugins_with_subdirs:
@@ -256,6 +321,18 @@ def test_no_relative_import_errors():
256321
except Exception as e:
257322
print(f"❌ SPL plugin test failed: {e}")
258323

324+
try:
325+
test_proxy_plugin()
326+
print("✅ Proxy plugin test passed")
327+
except Exception as e:
328+
print(f"❌ Proxy plugin test failed: {e}")
329+
330+
try:
331+
test_proxy_plugin_token_counts()
332+
print("✅ Proxy plugin token counts test passed")
333+
except Exception as e:
334+
print(f"❌ Proxy plugin token counts test failed: {e}")
335+
259336
try:
260337
test_plugin_subdirectory_imports()
261338
print("✅ Plugin subdirectory imports test passed")

0 commit comments

Comments
 (0)