2025-03-14 23:22:32 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
2025-10-13 17:34:13 +00:00
|
|
|
import logging
|
2025-03-14 23:22:32 +00:00
|
|
|
from pathlib import Path
|
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
|
|
2025-10-23 04:20:20 +00:00
|
|
|
import sentry_sdk
|
2025-03-14 23:22:32 +00:00
|
|
|
from aiservice.analytics.posthog import ph
|
|
|
|
|
from aiservice.common_utils import parse_python_version, validate_trace_id
|
2025-11-17 20:35:09 +00:00
|
|
|
from aiservice.env_specific import debug_log_sensitive_data, debug_log_sensitive_data_from_callable, llm_clients
|
2025-09-01 19:04:34 +00:00
|
|
|
from aiservice.models.aimodels import OPTIMIZE_MODEL, calculate_llm_cost
|
|
|
|
|
from log_features.log_event import update_optimization_cost
|
2025-03-14 23:22:32 +00:00
|
|
|
from log_features.log_features import log_features
|
2025-11-17 20:35:09 +00:00
|
|
|
from ninja import NinjaAPI, Schema
|
|
|
|
|
from openai.types.chat import ChatCompletionSystemMessageParam, ChatCompletionUserMessageParam
|
|
|
|
|
|
2025-08-05 23:32:19 +00:00
|
|
|
from optimizer.context_utils.optimizer_context import (
|
|
|
|
|
BaseOptimizerContext,
|
|
|
|
|
OptimizeErrorResponseSchema,
|
|
|
|
|
OptimizeResponseSchema,
|
|
|
|
|
)
|
2025-03-14 23:22:32 +00:00
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
2025-11-17 20:35:09 +00:00
|
|
|
from aiservice.models.aimodels import LLM
|
2025-03-14 23:22:32 +00:00
|
|
|
from openai.types.chat import (
|
|
|
|
|
ChatCompletionAssistantMessageParam,
|
|
|
|
|
ChatCompletionFunctionMessageParam,
|
|
|
|
|
ChatCompletionToolMessageParam,
|
|
|
|
|
)
|
|
|
|
|
|
2025-11-17 20:35:09 +00:00
|
|
|
from optimizer.context_utils.optimizer_context import OptimizeResponseItemSchema
|
2025-10-29 00:47:28 +00:00
|
|
|
|
2025-04-22 19:29:07 +00:00
|
|
|
|
|
|
|
|
optimize_line_profiler_api = NinjaAPI(urls_namespace="optimize-line-profiler")
|
|
|
|
|
|
2025-03-14 23:22:32 +00:00
|
|
|
|
|
|
|
|
# Get the directory of the current file
|
|
|
|
|
current_dir = Path(__file__).parent
|
|
|
|
|
SYSTEM_PROMPT = (current_dir / "system_prompt.md").read_text()
|
|
|
|
|
USER_PROMPT = (current_dir / "user_prompt.md").read_text()
|
|
|
|
|
|
2025-04-21 01:34:46 +00:00
|
|
|
|
2025-11-17 20:35:09 +00:00
|
|
|
async def optimize_python_code_line_profiler( # noqa: D417
|
2025-03-14 23:22:32 +00:00
|
|
|
user_id: str,
|
2025-09-01 19:04:34 +00:00
|
|
|
trace_id: str,
|
2025-03-14 23:22:32 +00:00
|
|
|
line_profiler_results: str,
|
2025-08-05 00:08:29 +00:00
|
|
|
ctx: BaseOptimizerContext,
|
2025-03-14 23:22:32 +00:00
|
|
|
dependency_code: str | None = None,
|
|
|
|
|
n: int = 1,
|
|
|
|
|
optimize_model: LLM = OPTIMIZE_MODEL,
|
2025-11-17 20:35:09 +00:00
|
|
|
lsp_mode: bool = False, # noqa: FBT001, FBT002
|
2025-08-02 12:46:38 +00:00
|
|
|
python_version: tuple[int, int, int] = (3, 12, 9),
|
2025-08-05 23:32:19 +00:00
|
|
|
) -> list[OptimizeResponseItemSchema]:
|
2025-03-14 23:22:32 +00:00
|
|
|
"""Optimize the given python code for performance using OpenAI's GPT-4o model.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2025-08-06 02:02:25 +00:00
|
|
|
- source_code (str): The python code to optimize.
|
2025-03-14 23:22:32 +00:00
|
|
|
- n (int): Number of optimization variants to generate. Default is 1.
|
|
|
|
|
|
|
|
|
|
Returns: - List[Tuple[Union[str, None], Union[str, None]]]: A list of tuples where the first element is the
|
|
|
|
|
optimized code and the second is the explanation.
|
|
|
|
|
|
|
|
|
|
"""
|
2025-10-13 17:34:13 +00:00
|
|
|
logging.info("/optimize: Optimizing python code line profile.")
|
2025-08-05 00:08:29 +00:00
|
|
|
debug_log_sensitive_data(f"Optimizing python code for user {user_id}:\n{ctx.source_code}")
|
2025-06-17 00:49:14 +00:00
|
|
|
if user_id in ["github|1235813", "github|1100399"] or lsp_mode:
|
|
|
|
|
# for Galileo and LSP mode, we only generate 5 LP optimizations
|
2025-06-06 20:07:16 +00:00
|
|
|
n = 5
|
2025-08-01 17:54:18 +00:00
|
|
|
|
2025-08-03 09:46:25 +00:00
|
|
|
python_version_str = ".".join(str(x) for x in python_version)
|
|
|
|
|
|
2025-03-14 23:22:32 +00:00
|
|
|
# TODO: Experiment with iterative approaches to optimization. Take the learnings from the testing phase into the
|
|
|
|
|
# next optimization iteration
|
|
|
|
|
# TODO: Experiment with iterative chain-of-thought generation. ask what is the
|
|
|
|
|
# function doing and then ask it to describe how to speed it up and then generate optimization
|
2025-08-02 12:46:38 +00:00
|
|
|
system_prompt = ctx.get_system_prompt(python_version_str=python_version_str)
|
2025-08-03 09:46:25 +00:00
|
|
|
user_prompt = ctx.get_user_prompt(dependency_code, line_profiler_results)
|
2025-07-28 14:56:03 +00:00
|
|
|
|
2025-08-02 12:46:38 +00:00
|
|
|
system_message = ChatCompletionSystemMessageParam(role="system", content=system_prompt)
|
2025-03-14 23:22:32 +00:00
|
|
|
user_message = ChatCompletionUserMessageParam(role="user", content=user_prompt)
|
|
|
|
|
messages: list[
|
|
|
|
|
ChatCompletionSystemMessageParam
|
|
|
|
|
| ChatCompletionUserMessageParam
|
|
|
|
|
| ChatCompletionAssistantMessageParam
|
|
|
|
|
| ChatCompletionToolMessageParam
|
|
|
|
|
| ChatCompletionFunctionMessageParam
|
|
|
|
|
] = [system_message, user_message]
|
2025-03-19 20:01:47 +00:00
|
|
|
debug_log_sensitive_data(f"This was the user prompt\n {user_prompt}\n")
|
2025-10-31 10:07:19 +00:00
|
|
|
# TODO: Verify if the context window length is within the model capability
|
2025-11-17 20:35:09 +00:00
|
|
|
llm_client = llm_clients[optimize_model.model_type]
|
2025-10-31 10:07:19 +00:00
|
|
|
try:
|
2025-11-17 20:35:09 +00:00
|
|
|
output = await llm_client.with_options(max_retries=3).chat.completions.create(
|
2025-10-31 10:07:19 +00:00
|
|
|
model=optimize_model.name, messages=messages, n=n
|
|
|
|
|
)
|
|
|
|
|
await update_optimization_cost(trace_id=trace_id, cost=calculate_llm_cost(output, optimize_model))
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logging.exception("OpenAI Code Generation error in optimizer-line-profiler")
|
|
|
|
|
sentry_sdk.capture_exception(e)
|
|
|
|
|
debug_log_sensitive_data(f"Failed to generate code for source:\n{ctx.source_code}")
|
|
|
|
|
return []
|
2025-03-14 23:22:32 +00:00
|
|
|
|
|
|
|
|
debug_log_sensitive_data(f"OpenAIClient optimization response:\n{output.model_dump_json(indent=2)}")
|
|
|
|
|
|
|
|
|
|
if output.usage is not None:
|
2025-03-31 21:34:42 +00:00
|
|
|
ph(
|
|
|
|
|
user_id,
|
|
|
|
|
"aiservice-optimize-line-profiler-openai-usage",
|
|
|
|
|
properties={"model": optimize_model.name, "n": n, "usage": output.usage.json()},
|
|
|
|
|
)
|
2025-03-14 23:22:32 +00:00
|
|
|
results = [content for op in output.choices if (content := op.message.content)]
|
2025-08-05 23:32:19 +00:00
|
|
|
optimization_response_items: list[OptimizeResponseItemSchema] = []
|
2025-03-14 23:22:32 +00:00
|
|
|
for result in results:
|
2025-08-02 12:46:38 +00:00
|
|
|
ctx.extract_code_and_explanation_from_llm_res(result)
|
2025-08-05 23:32:19 +00:00
|
|
|
res = ctx.parse_and_generate_candidate_schema()
|
|
|
|
|
if res is not None and ctx.is_valid_code():
|
|
|
|
|
optimization_response_items.append(res)
|
|
|
|
|
|
2025-08-02 17:41:07 +00:00
|
|
|
ctx.extracted_code_and_expl = None
|
|
|
|
|
ctx.parsed_code_and_explanation = None
|
2025-03-14 23:22:32 +00:00
|
|
|
|
2025-08-05 23:32:19 +00:00
|
|
|
return optimization_response_items
|
2025-03-14 23:22:32 +00:00
|
|
|
|
|
|
|
|
|
2025-03-15 03:58:27 +00:00
|
|
|
class OptimizeSchemaLP(Schema):
|
2025-03-14 23:22:32 +00:00
|
|
|
source_code: str
|
|
|
|
|
dependency_code: str | None
|
|
|
|
|
line_profiler_results: str | None
|
|
|
|
|
trace_id: str
|
|
|
|
|
python_version: str
|
|
|
|
|
experiment_metadata: dict[str, str] | None = None
|
|
|
|
|
codeflash_version: str | None = None
|
2025-06-17 00:49:14 +00:00
|
|
|
lsp_mode: bool = False
|
2025-09-22 22:32:44 +00:00
|
|
|
n_candidates_lp: int | None = 6
|
2025-03-14 23:22:32 +00:00
|
|
|
|
|
|
|
|
|
2025-03-15 03:58:27 +00:00
|
|
|
@optimize_line_profiler_api.post(
|
2025-03-14 23:22:32 +00:00
|
|
|
"/", response={200: OptimizeResponseSchema, 400: OptimizeErrorResponseSchema, 500: OptimizeErrorResponseSchema}
|
|
|
|
|
)
|
2025-11-17 20:35:09 +00:00
|
|
|
async def optimize(request, data: OptimizeSchemaLP) -> tuple[int, OptimizeResponseSchema | OptimizeErrorResponseSchema]: # noqa: ANN001
|
2025-03-31 21:34:42 +00:00
|
|
|
ph(request.user, "aiservice-optimize-called")
|
2025-08-05 00:08:29 +00:00
|
|
|
ctx: BaseOptimizerContext = BaseOptimizerContext.get_dynamic_context(SYSTEM_PROMPT, USER_PROMPT, data.source_code)
|
2025-03-14 23:22:32 +00:00
|
|
|
try:
|
|
|
|
|
python_version: tuple[int, int, int] = parse_python_version(data.python_version)
|
2025-11-17 20:35:09 +00:00
|
|
|
except: # noqa: E722
|
2025-03-14 23:22:32 +00:00
|
|
|
return 400, OptimizeErrorResponseSchema(
|
|
|
|
|
error="Invalid Python version, it should look like 3.x.x. We only support Python 3.9 and above."
|
|
|
|
|
)
|
|
|
|
|
try:
|
2025-08-05 23:32:19 +00:00
|
|
|
ctx.validate_and_parse_source_code(code=data.source_code, feature_version=python_version[:2])
|
2025-03-14 23:22:32 +00:00
|
|
|
except SyntaxError:
|
|
|
|
|
return 400, OptimizeErrorResponseSchema(
|
|
|
|
|
error="Invalid source code. It is not valid Python code. Please check syntax of your code."
|
|
|
|
|
)
|
|
|
|
|
if not validate_trace_id(data.trace_id):
|
|
|
|
|
return 400, OptimizeErrorResponseSchema(error="Invalid trace ID. Please provide a valid UUIDv4.")
|
2025-08-05 23:32:19 +00:00
|
|
|
optimization_response_items = await optimize_python_code_line_profiler(
|
2025-04-21 01:34:46 +00:00
|
|
|
user_id=request.user,
|
2025-09-01 19:04:34 +00:00
|
|
|
trace_id=data.trace_id,
|
2025-08-05 00:08:29 +00:00
|
|
|
ctx=ctx,
|
2025-04-21 01:34:46 +00:00
|
|
|
dependency_code=data.dependency_code,
|
|
|
|
|
line_profiler_results=data.line_profiler_results,
|
2025-09-23 01:22:10 +00:00
|
|
|
n=min(data.n_candidates_lp or 6, 8),
|
2025-06-17 00:49:14 +00:00
|
|
|
lsp_mode=data.lsp_mode,
|
2025-08-07 20:32:55 +00:00
|
|
|
python_version=python_version,
|
2025-03-14 23:22:32 +00:00
|
|
|
)
|
2025-08-05 23:32:19 +00:00
|
|
|
if len(optimization_response_items) == 0:
|
2025-03-31 21:34:42 +00:00
|
|
|
ph(request.user, "aiservice-optimize-no-optimizations-found")
|
2025-03-14 23:22:32 +00:00
|
|
|
debug_log_sensitive_data(f"No optimizations found for source:\n{data.source_code}")
|
|
|
|
|
return 500, OptimizeErrorResponseSchema(error="Error generating optimizations. Internal server error.")
|
2025-03-31 21:34:42 +00:00
|
|
|
ph(
|
|
|
|
|
request.user,
|
|
|
|
|
"aiservice-optimize-optimizations-found",
|
2025-08-05 23:32:19 +00:00
|
|
|
properties={"num_optimizations": len(optimization_response_items)},
|
2025-03-14 23:22:32 +00:00
|
|
|
)
|
|
|
|
|
|
2025-04-29 14:43:44 +00:00
|
|
|
if hasattr(request, "should_log_features") and request.should_log_features:
|
2025-03-14 23:22:32 +00:00
|
|
|
await log_features(
|
|
|
|
|
trace_id=data.trace_id,
|
|
|
|
|
user_id=request.user,
|
|
|
|
|
original_code=data.source_code,
|
|
|
|
|
dependency_code=data.dependency_code,
|
2025-04-21 01:34:46 +00:00
|
|
|
line_profiler_results=data.line_profiler_results,
|
2025-08-07 20:32:55 +00:00
|
|
|
optimizations_raw={
|
|
|
|
|
op_id: cei.code for op_id, cei in ctx.code_and_explanation_before_post_processing.items()
|
|
|
|
|
},
|
2025-08-05 23:32:19 +00:00
|
|
|
optimizations_post={cei.optimization_id: cei.source_code for cei in optimization_response_items},
|
2025-08-07 20:32:55 +00:00
|
|
|
explanations_raw={
|
|
|
|
|
op_id: cei.explanation for op_id, cei in ctx.code_and_explanation_before_post_processing.items()
|
|
|
|
|
},
|
2025-08-05 23:32:19 +00:00
|
|
|
explanations_post={cei.optimization_id: cei.explanation for cei in optimization_response_items},
|
2025-03-14 23:22:32 +00:00
|
|
|
experiment_metadata=data.experiment_metadata if data.experiment_metadata else None,
|
|
|
|
|
)
|
2025-08-05 23:32:19 +00:00
|
|
|
|
2025-08-07 20:32:55 +00:00
|
|
|
response = OptimizeResponseSchema(optimizations=optimization_response_items)
|
2025-03-14 23:22:32 +00:00
|
|
|
|
2025-11-17 20:35:09 +00:00
|
|
|
def log_response() -> None:
|
2025-03-14 23:22:32 +00:00
|
|
|
debug_log_sensitive_data(f"Response:\n{response.json()}")
|
|
|
|
|
for opt in response.optimizations:
|
|
|
|
|
debug_log_sensitive_data(f"Optimized source:\n{opt.source_code}")
|
|
|
|
|
debug_log_sensitive_data(f"Optimization explanation:\n{opt.explanation}")
|
|
|
|
|
|
|
|
|
|
debug_log_sensitive_data_from_callable(log_response)
|
2025-03-31 21:34:42 +00:00
|
|
|
ph(request.user, "aiservice-optimize-successful")
|
2025-03-14 23:22:32 +00:00
|
|
|
return 200, response
|