clean up prompts and improve quality

This commit is contained in:
jpremill committed 2026-03-06 08:46:03 -05:00
1 parent 9290cc18fd
commit 5b2da87f05
44 files changed
+6711 -414

No files matched your search

+264
View File
@@ -0,0 +1,264 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# Azure Functions localsettings file
local.settings.json
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
+5
View File
@@ -0,0 +1,5 @@
{
"recommendations": [
"ms-azuretools.vscode-azurefunctions"
]
}
+116
View File
@@ -0,0 +1,116 @@
using System.Net;
using System.Text.Json;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using Google.GenAI;
using System.Text.Json.Serialization;
namespace CognitiveClarity.api
{
public class AnalysisRequest
{
[JsonPropertyName("turnContext")]
public string TurnContext { get; set; } = string.Empty;
[JsonPropertyName("scenarioTitle")]
public string ScenarioTitle { get; set; } = string.Empty;
[JsonPropertyName("scenarioDescription")]
public string ScenarioDescription { get; set; } = string.Empty;
[JsonPropertyName("cbtFocus")]
public List<string> CBTFocus { get; set; } = new();
}
public class AnalysisFunction
{
private readonly ILogger _logger;
public AnalysisFunction(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<AnalysisFunction>();
}
[Function("ClinicalAnalysis")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "analysis")] HttpRequestData req)
{
_logger.LogInformation("C# HTTP trigger function processed a clinical analysis request.");
var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
var data = JsonSerializer.Deserialize<AnalysisRequest>(requestBody);
if (data == null || string.IsNullOrWhiteSpace(data.TurnContext))
{
var badResponse = req.CreateResponse(HttpStatusCode.BadRequest);
await badResponse.WriteStringAsync("Please provide turnContext in the request body");
return badResponse;
}
var apiKey = Environment.GetEnvironmentVariable("GEMINI_API_KEY");
if (string.IsNullOrEmpty(apiKey))
{
_logger.LogError("GEMINI_API_KEY is missing from environment variables.");
return req.CreateResponse(HttpStatusCode.InternalServerError);
}
// Initialize the Gemini Client
var ai = new GeminiClient(apiKey: apiKey);
var systemAnalysisPrompt = @"
You are a master clinical psychologist specializing in Cognitive Behavioral Therapy (CBT).
You are observing a roleplay session between a user and an AI character.
Analyze the user's latest response for:
1. Cognitive distortions present (if any).
2. CBT techniques successfully applied by the user (if any).
3. A brief, encouraging clinical insight or redirection (max 2 sentences).
Be precise, clinical, and constructive.
";
var analysisSystemInstruction = $@"{systemAnalysisPrompt}
SCENARIO CONTEXT:
Title: {data.ScenarioTitle}
Description: {data.ScenarioDescription}
CBT Focus Areas: {string.Join(", ", data.CBTFocus)}";
var schema = new Schema
{
Type = TypeEnum.Object,
Properties = new Dictionary<string, Schema>
{
{ "distortionsIdentified", new Schema { Type = TypeEnum.Array, Items = new Schema { Type = TypeEnum.String } } },
{ "techniquesUsed", new Schema { Type = TypeEnum.Array, Items = new Schema { Type = TypeEnum.String } } },
{ "clinicalInsight", new Schema { Type = TypeEnum.String } },
{ "feedbackType", new Schema { Type = TypeEnum.String, Enum = new List<string> { "Constructive", "Positive", "Neutral" } } }
},
Required = new List<string> { "distortionsIdentified", "techniquesUsed", "clinicalInsight", "feedbackType" }
};
try
{
var response = await ai.Models.GenerateContentAsync(
model: "gemini-3.0-flash",
contents: data.TurnContext,
new GenerateContentConfig
{
SystemInstruction = new Content { Parts = new List<Part> { new Part { Text = analysisSystemInstruction } } },
ResponseMimeType = "application/json",
ResponseSchema = schema
});
var successResponse = req.CreateResponse(HttpStatusCode.OK);
successResponse.Headers.Add("Content-Type", "application/json");
await successResponse.WriteStringAsync(response.Text!);
return successResponse;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to generate analysis via Gemini API.");
var errorResponse = req.CreateResponse(HttpStatusCode.InternalServerError);
await errorResponse.WriteStringAsync("Error communicating with Gemini API.");
return errorResponse;
}
}
}
}
+124
View File
@@ -0,0 +1,124 @@
using System.Net;
using System.Text.Json;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using Google.GenAI;
using System.Text.Json.Serialization;
namespace CognitiveClarity.api
{
public class ChatMessage
{
[JsonPropertyName("role")]
public string Role { get; set; } = string.Empty; // "user" or "model"
[JsonPropertyName("text")]
public string Text { get; set; } = string.Empty;
}
public class ChatRequest
{
[JsonPropertyName("history")]
public List<ChatMessage> History { get; set; } = new();
[JsonPropertyName("systemInstruction")]
public string SystemInstruction { get; set; } = string.Empty;
}
public class ChatFunction
{
private readonly ILogger _logger;
public ChatFunction(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<ChatFunction>();
}
[Function("ChatProgression")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "chat")] HttpRequestData req)
{
_logger.LogInformation("C# HTTP trigger function processed a chat progression request.");
var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
var data = JsonSerializer.Deserialize<ChatRequest>(requestBody);
if (data == null || data.History.Count == 0)
{
var badResponse = req.CreateResponse(HttpStatusCode.BadRequest);
await badResponse.WriteStringAsync("Please provide chat history in the request body");
return badResponse;
}
var apiKey = Environment.GetEnvironmentVariable("GEMINI_API_KEY");
if (string.IsNullOrEmpty(apiKey))
{
_logger.LogError("GEMINI_API_KEY is missing from environment variables.");
return req.CreateResponse(HttpStatusCode.InternalServerError);
}
// Initialize the Gemini Client
var ai = new GeminiClient(apiKey: apiKey);
// Construct contents from history
var contents = new List<Content>();
foreach (var msg in data.History)
{
contents.Add(new Content
{
Role = msg.Role,
Parts = new List<Part> { new Part { Text = msg.Text } }
});
}
var roleplaySystemInstruction = $@"{data.SystemInstruction}
[CONTEXT]
You are participating in a clinical CBT roleplay simulation.
The instructions above define your character, the scenario, and your internal CBT logic.
[TASK]
Respond to the user naturally in character to advance the roleplay scenario.
[CONSTRAINTS]
- CRITICAL: Stay strictly in character as the persona defined in the scenario.
- CRITICAL: NEVER provide clinical feedback, advice, or meta-commentary about the user's progress.
- CRITICAL: NEVER use phrases like ""Great job holding your boundary"" or ""Feedback:"".
- CRITICAL: Do not congratulate the user on using CBT techniques; simply react as the character would.
- If the user successfully concludes the scenario, end the dialogue naturally.
[OUTPUT REQUIREMENTS]
Return the character's direct dialogue response. Keep it conversational and proportional to the user's input.
";
try
{
var response = await ai.Models.GenerateContentAsync(
model: "gemini-3.0-flash",
contents: contents,
new GenerateContentConfig
{
SystemInstruction = new Content { Parts = new List<Part> { new Part { Text = roleplaySystemInstruction } } },
// Streaming isn't natively supported in standard HTTP Functions easily without Server-Sent Events (SSE).
// We will return a standard block response for now, which simplifies the architecture.
});
var successResponse = req.CreateResponse(HttpStatusCode.OK);
successResponse.Headers.Add("Content-Type", "application/json");
// Return just the text in a JSON wrapper
var resultObj = new { text = response.Text };
await successResponse.WriteStringAsync(JsonSerializer.Serialize(resultObj));
return successResponse;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to generate chat progression via Gemini API.");
var errorResponse = req.CreateResponse(HttpStatusCode.InternalServerError);
await errorResponse.WriteStringAsync("Error communicating with Gemini API.");
return errorResponse;
}
}
}
}
+14
View File
@@ -0,0 +1,14 @@
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = FunctionsApplication.CreateBuilder(args);
builder.ConfigureFunctionsWebApplication();
builder.Services
.AddApplicationInsightsTelemetryWorkerService()
.ConfigureFunctionsApplicationInsights();
builder.Build().Run();
+9
View File
@@ -0,0 +1,9 @@
{
"profiles": {
"api": {
"commandName": "Project",
"commandLineArgs": "--port 7145",
"launchBrowser": false
}
}
}
+130
View File
@@ -0,0 +1,130 @@
using System.Net;
using System.Text.Json;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using Google.GenAI;
using System.Text.Json.Serialization;
namespace CognitiveClarity.api
{
public class ScenarioRequest
{
[JsonPropertyName("prompt")]
public string Prompt { get; set; } = string.Empty;
[JsonPropertyName("selectedConcepts")]
public List<string> SelectedConcepts { get; set; } = new();
[JsonPropertyName("difficulty")]
public string Difficulty { get; set; } = "Intermediate";
}
public class ScenarioFunction
{
private readonly ILogger _logger;
public ScenarioFunction(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<ScenarioFunction>();
}
[Function("GenerateScenario")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "scenario/generate")] HttpRequestData req)
{
_logger.LogInformation("C# HTTP trigger function processed a scenario generation request.");
var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
var data = JsonSerializer.Deserialize<ScenarioRequest>(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 = Environment.GetEnvironmentVariable("GEMINI_API_KEY");
if (string.IsNullOrEmpty(apiKey))
{
_logger.LogError("GEMINI_API_KEY is missing from environment variables.");
return req.CreateResponse(HttpStatusCode.InternalServerError);
}
// Initialize the Gemini Client
var ai = new GeminiClient(apiKey: apiKey);
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 conforming exactly to the requested schema. Ensure all arrays and nested objects are populated correctly.
";
var schema = new Schema
{
Type = TypeEnum.Object,
Properties = new Dictionary<string, Schema>
{
{ "id", new Schema { Type = TypeEnum.String } },
{ "title", new Schema { Type = TypeEnum.String } },
{ "characterName", new Schema { Type = TypeEnum.String } },
{ "description", new Schema { Type = TypeEnum.String } },
{ "category", new Schema { Type = TypeEnum.String, Enum = new List<string> { "Social Anxiety", "Workplace", "Relationships", "Self-Talk" } } },
{ "difficulty", new Schema { Type = TypeEnum.String, Enum = new List<string> { "Beginner", "Intermediate", "Advanced" } } },
{ "systemInstruction", new Schema { Type = TypeEnum.String } },
{ "initialMessage", new Schema { Type = TypeEnum.String } },
{ "cbtFocus", new Schema { Type = TypeEnum.Array, Items = new Schema { Type = TypeEnum.String } } }
},
Required = new List<string> { "id", "title", "characterName", "description", "category", "difficulty", "systemInstruction", "initialMessage", "cbtFocus" }
};
var promptText = $"Generate a clinical scenario for: {(string.IsNullOrWhiteSpace(data.Prompt) ? "A scenario exploring the chosen CBT concepts." : data.Prompt)}";
try
{
var response = await ai.Models.GenerateContentAsync(
model: "gemini-3.0-flash",
contents: promptText,
new GenerateContentConfig
{
SystemInstruction = new Content { Parts = new List<Part> { new Part { Text = systemInstruction } } },
ResponseMimeType = "application/json",
ResponseSchema = schema
});
var successResponse = req.CreateResponse(HttpStatusCode.OK);
successResponse.Headers.Add("Content-Type", "application/json");
await successResponse.WriteStringAsync(response.Text!);
return successResponse;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to generate scenario via Gemini API.");
var errorResponse = req.CreateResponse(HttpStatusCode.InternalServerError);
await errorResponse.WriteStringAsync("Error communicating with Gemini API.");
return errorResponse;
}
}
}
}
+21
View File
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Google.GenAI" Version="1.3.0" />
<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.23.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.50.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="2.50.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.1.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.6" />
</ItemGroup>
</Project>
+12
View File
@@ -0,0 +1,12 @@
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
},
"enableLiveMetricsFilters": true
}
}
}