Feature/service agent#87
Feature/service agent#87officialid130-13e13 wants to merge 6 commits intothoth-tech:Feature/AI-Suggestionfrom
Conversation
jtalev
left a comment
There was a problem hiding this comment.
Generally a good structure but needs some polish and a rethink about where we want to use the service and what we want to do with the response, do we store it and return the values when a user visits their unit dashboard? are we returning the predicted effort on each request and its a transient value?
I think we need to store the outputs from the service in a db table (task_definitions maybe?) but happy to make this another ticket if you want to tidy up the comments Ive left here for you so we can get some work ticked off for your assessments
| return | ||
| end | ||
|
|
||
| uri = URI("http://localhost:8080/predictions/effort-predictor") |
There was a problem hiding this comment.
Thanks for pointing this out — we already have TORCHSERVE_URL defined in api.env. I will update the controller to reference that environment variable instead of hard‑coding the URI, so the endpoint remains configurable across environments.
|
|
||
| # POST /tasks/predict_effort | ||
| def predict_effort | ||
| features = params[:features] |
There was a problem hiding this comment.
how do we know what task we are predicting effort for? we dont seem to be identifying tasks in any way here
There was a problem hiding this comment.
At the moment we are using a dummy model that just accepts 10 inputs and returns a score, so we haven’t tied predictions to specific tasks yet. Once we train the real model (regression or NLP), we’ll likely need to pass a task_id (and possibly unit_id) so predictions can be persisted against the correct records. Happy to keep this as a follow‑up discussion, but for now the dummy model is just for scaffolding.
| prediction["predicted_effort"] || prediction.values.first | ||
| else | ||
| prediction | ||
| end |
There was a problem hiding this comment.
why not delegate this to your effort_prediction_service? its doing the same thing as here so you have implemented the same thing twice which wont be maintained
There was a problem hiding this comment.
Thanks for pointing this out — agreed that the prediction parsing logic is duplicated in the controller and service. I will refactor the controller to delegate to EffortPredictionService so the parsing is maintained in one place.
| # effort prediction endpoint for task.. | ||
| desc 'Predict effort for a task' | ||
| params do | ||
| requires :features, type: Array[Float], desc: 'Feature values' |
There was a problem hiding this comment.
need to include ID for task getting prediction
There was a problem hiding this comment.
At the moment we are using a dummy model that just accepts 10 inputs and returns a score, so we haven’t tied predictions to specific tasks yet. Once we train and integrate the real model (regression or NLP), we will update the API contract to include task_id (and possibly unit_id) so predictions can be persisted against the correct records. For now, the dummy model is just scaffolding.
| post :predict_effort do | ||
| features = params[:features] | ||
| prediction = EffortPredictionService.predict(features) | ||
| { predicted_effort: prediction } |
There was a problem hiding this comment.
what are we doing with the result of the prediction? we should be saving it somewhere
There was a problem hiding this comment.
Thanks for pointing it out — at the moment we are just returning the prediction as a transient value. Once we move beyond the dummy model and integrate the real predictor, we will persist the results in the DB (likely task_definitions) so they can be reused on dashboards and for comparative analysis. For now, I will keep this endpoint simple and return the score, but we will create a follow‑up ticket to implement persistence.
|
|
||
| PLATFORMS | ||
| aarch64-linux | ||
| arm64-darwin-23 |
There was a problem hiding this comment.
Again, the arm64-darwin-23 entry in the PLATFORMS section was added automatically by Bundler when generating the lockfile on macOS ARM. It is not manually introduced as Bundler records the platform so native gems like Nokogiri/ffi can be resolved correctly across different environments.
| ml_services/logs/access_log.24-Mar.log.gz | ||
| ml_services/logs/access_log.log | ||
| ml_services/logs/model_log.24-Mar.log.gz | ||
| ml_services/logs/model_log.log | ||
| ml_services/logs/model_metrics.log | ||
| ml_services/logs/ts_log.24-Mar.log.gz | ||
| ml_services/logs/ts_log.log | ||
| ml_services/logs/ts_metrics.24-Mar.log.gz | ||
| ml_services/logs/ts_metrics.log | ||
| ml_services/logs/config/20260324175502069-startup.cfg | ||
| ml_services/logs/config/20260324184851096-shutdown.cfg | ||
| ml_services/logs/config/20260324184854550-startup.cfg | ||
| ml_services/logs/config/20260325144256690-shutdown.cfg | ||
| ml_services/logs/config/20260325144319876-startup.cfg | ||
| ml_services/logs/config/20260325144545311-shutdown.cfg | ||
| ml_services/logs/config/20260325144552586-startup.cfg | ||
| ml_services/logs/config/20260325144822051-shutdown.cfg | ||
| ml_services/logs/config/20260325144903054-startup.cfg | ||
| ml_services/logs/config/20260325163409679-shutdown.cfg | ||
| ml_services/logs/config/20260325172728867-startup.cfg |
There was a problem hiding this comment.
you can do something like ml_services/logs/* to ignore all
There was a problem hiding this comment.
I will simplify the .gitignore entry to ml_services/logs/* so all generated logs/config files are consistently ignored, instead of listing them individually.
| { | ||
| "management": { | ||
| "key": "ZT_SKghL", | ||
| "expiration time": "2026-04-13T15:16:21.380749386Z" | ||
| }, | ||
| "inference": { | ||
| "key": "AJVoXRTf", | ||
| "expiration time": "2026-04-13T15:16:21.380728803Z" | ||
| }, | ||
| "API": { | ||
| "key": "hIvWRijq" | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
committing API keys is generally bad practice, if your not worried about it it should be fine i guess
There was a problem hiding this comment.
This key_file.json is auto‑generated by TorchServe for API key management (management, inference, and API keys). It changes every time we run our containers, so committing it is not necessary and could expose sensitive values. We will utilise the inference key via environment variables instead, and add this file to .gitignore so it is excluded from source control.
|
|
||
|
|
||
| # Return JSON response | ||
| return [json.dumps({"predicted_effort": output})] |
There was a problem hiding this comment.
i think this is returning a json string inside a list, you probably only need to return {"predicted_effort": output} here (the object itself)
There was a problem hiding this comment.
I agreed that returning json.dumps wraps the JSON string unnecessarily. For now this was needed to get curl responses working with the dummy model, but once we integrate the trained model we will update the handler to return the object itself ({"predicted_effort": output}) so TorchServe can serialize it cleanly. That will also give us flexibility to include additional fields like confidence or task_id.
| return [json.dumps({"predicted_effort": output})] | ||
|
|
||
| except Exception as e: | ||
| return [json.dumps({"error": str(e)})] |
There was a problem hiding this comment.
same here, will make it easier on the ruby side to parse the response
There was a problem hiding this comment.
Thanks for pointing this out — I agree that returning a dict directly ({"predicted_effort": output}) would be cleaner and easier to parse. At the moment we are using a dummy model, and wrapping the response in json.dumps was needed to get curl responses working reliably. Once we integrate the trained model and have more structured outputs (such as effort, confidence, task_id etc.), we will update the handler to return the object itself so TorchServe can serialize it cleanly.
Thanks for the feedback — our plan is to expose a ‘Run Prediction’ action that calls the API and produces scores for all tasks in a unit. We’ll persist those outputs in the DB (likely task_definitions) so we can calculate relative effort percentages across tasks. For now I will tidy up the comments so this PR can be merged, and we can create a follow‑up ticket to implement the persistence logic. |
Introduced Effort Predictor service agent with new ml_services folder, handler, and TaskDownloadController to predict the effort estimation for a task using dummy model.
Added Swagger endpoint for testing and validating the response.