CKT commited on
Commit
eeb3194
·
1 Parent(s): 4c571f8

update_answers

Browse files
Files changed (2) hide show
  1. app.py +86 -9
  2. data/profiles.json +14 -0
app.py CHANGED
@@ -3,7 +3,7 @@ import random
3
  import json
4
  import uuid
5
  import os # Added for path joining
6
- from datetime import datetime # Added for timestamps
7
 
8
  # --- Start of JSON I/O Helper Functions ---
9
 
@@ -63,7 +63,7 @@ def save_json_data(filepath, data):
63
 
64
  # --- Start of MCP Matchmaker Tools ---
65
 
66
- def profile_questionnaire_func(request: gr.Request):
67
  """
68
  Generates and returns a profile questionnaire, a new public profile_id,
69
  and a new private auth_id. Also creates an initial profile stub.
@@ -80,7 +80,8 @@ def profile_questionnaire_func(request: gr.Request):
80
 
81
  # 3. Create new profile entry in profiles.json
82
  profiles = load_json_data(PROFILES_FILE, default_data={})
83
- timestamp = datetime.utcnow().isoformat() + "Z" # ISO 8601 format
 
84
 
85
  profiles[profile_id] = {
86
  "profile_id": profile_id,
@@ -97,7 +98,7 @@ def profile_questionnaire_func(request: gr.Request):
97
  # Decide how to handle this error - maybe return an error to the client?
98
 
99
  # 4. Return IDs, questionnaire data, and instructions
100
- instructions_for_agent = "You have received a `profile_id` (public identifier for this user\'s profile) and an `auth_id` (private key for authentication for this user). Store both securely. The `auth_id` must be sent as an `X-Auth-ID` header in subsequent requests that require authentication for this user (e.g., `update_profile_answers`, `get_matches`, `get_messages`, `get_profile` for own profile, `send_message`). The `profile_id` is used to publicly identify this user to others (e.g., in matches, or when sending/receiving messages)."
101
  instructions_for_user = "Your profile creation process has started! You\'ve been assigned a unique Profile ID and a secret Auth ID. Your AI agent will use these to manage your profile and interactions."
102
 
103
  return {
@@ -108,6 +109,72 @@ def profile_questionnaire_func(request: gr.Request):
108
  "instructions_for_user": instructions_for_user
109
  }
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  # --- End of MCP Matchmaker Tools ---
112
 
113
  # Original example functions from the user's app.py for context
@@ -137,18 +204,28 @@ headers_demo = gr.Interface(
137
 
138
  # --- Start of New Profile Questionnaire Interface ---
139
  profile_questionnaire_demo = gr.Interface(
140
- fn=profile_questionnaire_func,
141
  inputs=None, # No direct input from user for this one, it's triggered by the agent
142
  outputs=gr.JSON(label="Questionnaire, IDs, and Instructions"),
143
- title="MCP - Profile Questionnaire",
144
- description="Generates a new user profile questionnaire, Profile ID, and Auth ID. (Agent-triggered MCP tool)"
145
  )
146
  # --- End of New Profile Questionnaire Interface ---
147
 
 
 
 
 
 
 
 
 
 
 
148
  # Adjusted TabbedInterface to include the new tool
149
  demo = gr.TabbedInterface(
150
- [profile_questionnaire_demo, headers_demo],
151
- ["MCP - Profile Questionnaire", "Headers Debug"]
152
  )
153
 
154
  if __name__ == "__main__":
 
3
  import json
4
  import uuid
5
  import os # Added for path joining
6
+ from datetime import datetime, timezone # Added timezone for UTC consistency
7
 
8
  # --- Start of JSON I/O Helper Functions ---
9
 
 
63
 
64
  # --- Start of MCP Matchmaker Tools ---
65
 
66
+ def profile_questionnaire(request: gr.Request):
67
  """
68
  Generates and returns a profile questionnaire, a new public profile_id,
69
  and a new private auth_id. Also creates an initial profile stub.
 
80
 
81
  # 3. Create new profile entry in profiles.json
82
  profiles = load_json_data(PROFILES_FILE, default_data={})
83
+ # Ensure timestamp is timezone-aware (UTC)
84
+ timestamp = datetime.now(timezone.utc).isoformat()
85
 
86
  profiles[profile_id] = {
87
  "profile_id": profile_id,
 
98
  # Decide how to handle this error - maybe return an error to the client?
99
 
100
  # 4. Return IDs, questionnaire data, and instructions
101
+ instructions_for_agent = "You have received a `profile_id` (public identifier for this user\'s profile) and an `auth_id` (private key for authentication for this user). Please ask the user to store both securely. The `auth_id` must be sent as an `X-Auth-ID` header in subsequent requests that require authentication for this user (e.g., `update_profile_answers`, `get_matches`, `get_messages`, `get_profile` for own profile, `send_message`). The `profile_id` is used to publicly identify this user to others (e.g., in matches, or when sending/receiving messages)."
102
  instructions_for_user = "Your profile creation process has started! You\'ve been assigned a unique Profile ID and a secret Auth ID. Your AI agent will use these to manage your profile and interactions."
103
 
104
  return {
 
109
  "instructions_for_user": instructions_for_user
110
  }
111
 
112
+ def update_profile_answers(answers_payload_str: str, request: gr.Request):
113
+ """
114
+ Updates a user's profile based on questionnaire answers.
115
+ Requires X-Auth-ID header for authentication.
116
+ Answers_payload_str is expected to be a JSON string.
117
+ """
118
+ auth_id_header = request.headers.get("x-auth-id") # Headers are lowercased by Gradio/Starlette
119
+ if not auth_id_header:
120
+ return {"status": "error", "message": "Authentication failed: X-Auth-ID header is missing."}
121
+
122
+ try:
123
+ answers_payload = json.loads(answers_payload_str)
124
+ if not isinstance(answers_payload, dict):
125
+ raise json.JSONDecodeError("Payload is not a dictionary.", answers_payload_str, 0)
126
+ except json.JSONDecodeError as e:
127
+ return {"status": "error", "message": f"Invalid JSON format in answers_payload: {e}"}
128
+
129
+ profiles = load_json_data(PROFILES_FILE, default_data={})
130
+ user_profile = None
131
+ target_profile_id = None
132
+
133
+ for pid, profile_data in profiles.items():
134
+ if profile_data.get("auth_id") == auth_id_header:
135
+ user_profile = profile_data
136
+ target_profile_id = pid
137
+ break
138
+
139
+ if not user_profile:
140
+ return {"status": "error", "message": "Authentication failed: Invalid X-Auth-ID."}
141
+
142
+ questionnaire = load_json_data(QUESTIONNAIRE_FILE, default_data={"questions": []})
143
+ questions_map = {q["id"]: q for q in questionnaire.get("questions", [])}
144
+
145
+ updated_fields = False
146
+ for question_id, answer_value in answers_payload.items():
147
+ question_details = questions_map.get(question_id)
148
+ if not question_details:
149
+ print(f"Warning: Received answer for unknown question_id '{question_id}'. Skipping.")
150
+ continue
151
+
152
+ if question_details.get("purpose") == "metadata":
153
+ field_to_map = question_details.get("maps_to_field")
154
+ if field_to_map and field_to_map in user_profile:
155
+ user_profile[field_to_map] = answer_value
156
+ updated_fields = True
157
+ else:
158
+ print(f"Warning: Metadata question '{question_id}' has no valid 'maps_to_field' or field not in profile. Skipping.")
159
+ elif question_details.get("purpose") == "matchmaking":
160
+ user_profile["answers"][question_id] = answer_value
161
+ updated_fields = True
162
+ else:
163
+ print(f"Warning: Question '{question_id}' has unknown purpose '{question_details.get('purpose')}'. Skipping.")
164
+
165
+ if updated_fields:
166
+ user_profile["updated_at"] = datetime.now(timezone.utc).isoformat()
167
+ profiles[target_profile_id] = user_profile # Update the profile in the main dictionary
168
+ if not save_json_data(PROFILES_FILE, profiles):
169
+ return {"status": "error", "message": "Failed to save profile updates."}
170
+
171
+ return {
172
+ "status": "success",
173
+ "message": "Profile updated successfully.",
174
+ "instructions_for_agent": "Profile updated. Next recommended steps: call `get_matches` to find potential matches, or `get_profile` (using the user's `profile_id`) to view the updated profile.",
175
+ "instructions_for_user": "Your profile has been updated! You can now look for matches or view your profile."
176
+ }
177
+
178
  # --- End of MCP Matchmaker Tools ---
179
 
180
  # Original example functions from the user's app.py for context
 
204
 
205
  # --- Start of New Profile Questionnaire Interface ---
206
  profile_questionnaire_demo = gr.Interface(
207
+ fn=profile_questionnaire,
208
  inputs=None, # No direct input from user for this one, it's triggered by the agent
209
  outputs=gr.JSON(label="Questionnaire, IDs, and Instructions"),
210
+ title="Profile Questionnaire",
211
+ description="Generates a new user profile and returns a questionnaire, Profile ID, and Auth ID. (Agent-triggered MCP tool)"
212
  )
213
  # --- End of New Profile Questionnaire Interface ---
214
 
215
+ # --- Start of Update Profile Answers Interface ---
216
+ update_profile_answers_demo = gr.Interface(
217
+ fn=update_profile_answers,
218
+ inputs=[gr.Textbox(label="Answers Payload (JSON string)", lines=5)], # Agent provides this as a JSON string
219
+ outputs=gr.JSON(label="Update Status"),
220
+ title="Update Profile Answers",
221
+ description="Updates a user's profile with answers to the questionnaire. Requires X-Auth-ID header."
222
+ )
223
+ # --- End of Update Profile Answers Interface ---
224
+
225
  # Adjusted TabbedInterface to include the new tool
226
  demo = gr.TabbedInterface(
227
+ [profile_questionnaire_demo, update_profile_answers_demo, headers_demo],
228
+ ["Profile Questionnaire", "Update Profile Answers", "Headers Debug"]
229
  )
230
 
231
  if __name__ == "__main__":
data/profiles.json CHANGED
@@ -8,5 +8,19 @@
8
  "profile_image_filename": null,
9
  "profile_summary": "",
10
  "answers": {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  }
12
  }
 
8
  "profile_image_filename": null,
9
  "profile_summary": "",
10
  "answers": {}
11
+ },
12
+ "user_y38k71ox": {
13
+ "profile_id": "user_y38k71ox",
14
+ "auth_id": "cc175293-a9eb-4db0-ad2f-f3ba7a0dab25",
15
+ "created_at": "2025-06-05T16:49:53.871722+00:00",
16
+ "updated_at": "2025-06-05T17:00:46.839293+00:00",
17
+ "name": "User",
18
+ "profile_image_filename": "placeholder.png",
19
+ "profile_summary": "I am very cool, please date me.",
20
+ "answers": {
21
+ "q_hobby": "My main hobby is coding.",
22
+ "q_looking_for": "I'm looking for someone who is as cool as me.",
23
+ "q_vibe": "My general vibe is... coding."
24
+ }
25
  }
26
  }