Skip to content

Feature/service agent#87

Open
officialid130-13e13 wants to merge 6 commits intothoth-tech:Feature/AI-Suggestionfrom
officialid130-13e13:feature/service-agent
Open

Feature/service agent#87
officialid130-13e13 wants to merge 6 commits intothoth-tech:Feature/AI-Suggestionfrom
officialid130-13e13:feature/service-agent

Conversation

@officialid130-13e13
Copy link
Copy Markdown

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.

Copy link
Copy Markdown

@jtalev jtalev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put this in an environment variable

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do we know what task we are predicting effort for? we dont seem to be identifying tasks in any way here

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread app/api/tasks_api.rb
# effort prediction endpoint for task..
desc 'Predict effort for a task'
params do
requires :features, type: Array[Float], desc: 'Feature values'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to include ID for task getting prediction

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread app/api/tasks_api.rb
post :predict_effort do
features = params[:features]
prediction = EffortPredictionService.predict(features)
{ predicted_effort: prediction }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are we doing with the result of the prediction? we should be saving it somewhere

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread Gemfile.lock

PLATFORMS
aarch64-linux
arm64-darwin-23
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this one

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread .gitignore Outdated
Comment on lines +44 to +63
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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can do something like ml_services/logs/* to ignore all

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will simplify the .gitignore entry to ml_services/logs/* so all generated logs/config files are consistently ignored, instead of listing them individually.

Comment thread ml_services/key_file.json
Comment on lines +1 to +13
{
"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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

committing API keys is generally bad practice, if your not worried about it it should be fine i guess

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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})]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this is returning a json string inside a list, you probably only need to return {"predicted_effort": output} here (the object itself)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)})]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, will make it easier on the ruby side to parse the response

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@officialid130-13e13
Copy link
Copy Markdown
Author

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

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.

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.

2 participants