using System.Net; using System.Net.Http.Headers; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; using Microsoft.Extensions.Logging; namespace CognitiveClarity.api { public class ScenarioRequest { [JsonPropertyName("prompt")] public string Prompt { get; set; } = string.Empty; [JsonPropertyName("selectedConcepts")] public List SelectedConcepts { get; set; } = new(); [JsonPropertyName("difficulty")] public string Difficulty { get; set; } = "Intermediate"; [JsonPropertyName("model")] public string Model { get; set; } = "google/gemini-3.8-flash"; } public class ScenarioFunction { private readonly ILogger _logger; private readonly IHttpClientFactory _httpClientFactory; public ScenarioFunction(ILoggerFactory loggerFactory, IHttpClientFactory httpClientFactory) { _logger = loggerFactory.CreateLogger(); _httpClientFactory = httpClientFactory; } [Function("GenerateScenario")] public async Task Run( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "scenario/generate")] HttpRequestData req) { _logger.LogInformation("C# HTTP trigger function processed a scenario generation request via OpenRouter."); var requestBody = await new StreamReader(req.Body).ReadToEndAsync(); var data = JsonSerializer.Deserialize(requestBody); if (data == null || (string.IsNullOrWhiteSpace(data.Prompt) && data.SelectedConcepts.Count == 0)) { var badResponse = req.CreateResponse(HttpStatusCode.BadRequest); await badResponse.WriteStringAsync("Please pass a prompt or concepts in the request body"); return badResponse; } var apiKey = System.Environment.GetEnvironmentVariable("OPENROUTER_API_KEY"); if (string.IsNullOrEmpty(apiKey)) { _logger.LogError("OPENROUTER_API_KEY is missing from environment variables."); return req.CreateResponse(HttpStatusCode.InternalServerError); } var selectedContext = data.SelectedConcepts.Count > 0 ? $"Focus specifically on these CBT concepts: {string.Join(", ", data.SelectedConcepts)}." : ""; var systemInstruction = $@" [CONTEXT] You are a Clinical CBT Scenario Architect. The user requires a structured clinical simulation designed to address specific cognitive distortions and behavioral challenges. Clinical Focus: {selectedContext} [TASK] Transform the user's prompt into a cohesive, structured clinical scenario that sets up a targeted roleplay environment. [CONSTRAINTS] - CATEGORIZATION: You MUST categorize the scenario appropriately (Social Anxiety, Workplace, Relationships, or Self-Talk). - DIFFICULTY CALIBRATION: The requested difficulty is '{data.Difficulty}'. - 'Beginner': Low stakes, high support, clear-cut distortions, AI character is cooperative. - 'Intermediate': Ambiguous stakes, moderate social friction, subtle distortions, AI character has specific needs. - 'Advanced': High emotional triggers, persistent clinical resistance, complex cognitive patterns. - INTERNAL LOGIC: Provide a detailed ""INTERNAL CBT LOGIC"" section instructing the AI character how to react to the user's progress or cognitive lapses during the future roleplay. - CHARACTER VOICE: Define a realistic, emotionally consistent voice profile. [OUTPUT REQUIREMENTS] Return ONLY a valid JSON object matching this schema: {{ ""id"": ""string"", ""title"": ""string"", ""characterName"": ""string"", ""description"": ""string"", ""category"": ""Social Anxiety"" | ""Workplace"" | ""Relationships"" | ""Self-Talk"", ""difficulty"": ""Beginner"" | ""Intermediate"" | ""Advanced"", ""systemInstruction"": ""string"", ""initialMessage"": ""string"", ""cbtFocus"": [""string""] }}"; var promptText = $"Generate a clinical scenario for: {(string.IsNullOrWhiteSpace(data.Prompt) ? "A scenario exploring the chosen CBT concepts." : data.Prompt)}"; var payload = new { model = string.IsNullOrEmpty(data.Model) ? "google/gemini-3.8-flash" : data.Model, messages = new object[] { new { role = "system", content = systemInstruction }, new { role = "user", content = promptText } }, response_format = new { type = "json_object" }, temperature = 0.7 }; try { var client = _httpClientFactory.CreateClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey); client.DefaultRequestHeaders.Add("HTTP-Referer", "https://cognitive-clarity.app"); client.DefaultRequestHeaders.Add("X-Title", "Cognitive Clarity"); var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"); var response = await client.PostAsync("https://openrouter.ai/api/v1/chat/completions", content); if (!response.IsSuccessStatusCode) { var errText = await response.Content.ReadAsStringAsync(); _logger.LogError("OpenRouter returned error {Status}: {Error}", response.StatusCode, errText); var errResponse = req.CreateResponse(response.StatusCode); await errResponse.WriteStringAsync(errText); return errResponse; } var resJson = await response.Content.ReadAsStringAsync(); using var doc = JsonDocument.Parse(resJson); var scenarioJson = doc.RootElement .GetProperty("choices")[0] .GetProperty("message") .GetProperty("content") .GetString() ?? "{}"; var successResponse = req.CreateResponse(HttpStatusCode.OK); successResponse.Headers.Add("Content-Type", "application/json"); await successResponse.WriteStringAsync(scenarioJson); return successResponse; } catch (Exception ex) { _logger.LogError(ex, "Failed to generate scenario via OpenRouter API."); var errorResponse = req.CreateResponse(HttpStatusCode.InternalServerError); await errorResponse.WriteStringAsync("Error communicating with OpenRouter API."); return errorResponse; } } } }