Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions samples/llm-usage/.devproxy/azure-ai-prices.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v1.0.0/openaitelemetryplugin.pricesfile.schema.json",
"prices": {
"gpt-4.1-2025-04-14": {
"input": 0.97,
"output": 3.87
},
"llama3.2": {
"input": 0.97,
"output": 3.87
}
}
}
55 changes: 55 additions & 0 deletions samples/llm-usage/.devproxy/devproxyrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v1.0.0/rc.schema.json",
"plugins": [
{
"name": "LatencyPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
"configSection": "apiLatencyPlugin",
"urlsToWatch": [
"http://api.ecs.eu/*"
]
},
{
"name": "OpenAITelemetryPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
"configSection": "openAITelemetryPlugin"
},
{
"name": "CrudApiPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
"configSection": "feedbackApi",
"urlsToWatch": [
"http://api.ecs.eu/feedback"
]
},
{
"name": "MarkdownReporter",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll"
}
],
"urlsToWatch": [
"https://models.github.ai/inference/chat/completions*"
],
"feedbackApi": {
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v1.0.0/crudapiplugin.schema.json",
"apiFile": "feedback-api.json"
},
"apiLatencyPlugin": {
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v1.0.0/latencyplugin.schema.json",
"minMs": 200,
"maxMs": 500
},
"openAITelemetryPlugin": {
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v1.0.0/openaitelemetryplugin.schema.json",
"currency": "EUR",
"includeCosts": true,
"pricesFile": "azure-ai-prices.json"
},
"logLevel": "trace",
"newVersionNotification": "stable",
"showSkipMessages": true
}
13 changes: 13 additions & 0 deletions samples/llm-usage/.devproxy/feedback-api.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v1.0.0/crudapiplugin.apifile.schema.json",
"actions": [
{
"action": "getAll"
},
{
"action": "create"
}
],
"baseUrl": "http://api.ecs.eu/feedback",
"dataFile": "feedback-data.json"
}
47 changes: 47 additions & 0 deletions samples/llm-usage/.devproxy/feedback-data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
[
{
"id": 1,
"feedback": "The presentation slides were very well designed and informative. Would be great to have them shared with all attendees afterward.",
"date": "2025-05-14T15:30:00"
},
{
"id": 2,
"feedback": "The speaker was excellent but the Q&A session was cut short. Please allocate more time for questions in future events.",
"date": "2025-05-14T16:45:00"
},
{
"id": 3,
"feedback": "Try to speak more slowly and enunciate more clearly. Some attendees in the back had trouble hearing you.",
"date": "2025-05-14T14:20:00"
},
{
"id": 4,
"feedback": "The coffee during the break was amazing!",
"date": "2025-05-14T10:15:00"
},
{
"id": 5,
"feedback": "Include more real-world examples in your next presentation to better illustrate the concepts.",
"date": "2025-05-15T09:30:00"
},
{
"id": 6,
"feedback": "The room was too cold, please adjust the temperature for tomorrow's sessions.",
"date": "2025-05-15T11:10:00"
},
{
"id": 7,
"feedback": "I really liked the speaker's tie.",
"date": "2025-05-15T13:45:00"
},
{
"id": 8,
"feedback": "Your slides had too much text. Consider using more visuals and less text for better audience engagement.",
"date": "2025-05-15T10:20:00"
},
{
"id": 9,
"feedback": "The session was okay.",
"date": "2025-05-15T13:47:00"
}
]
64 changes: 64 additions & 0 deletions samples/llm-usage/.devproxy/simulate-ai.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v1.0.0/rc.schema.json",
"plugins": [
{
"name": "LatencyPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
"configSection": "apiLatencyPlugin",
"urlsToWatch": [
"http://api.ecs.eu/*"
]
},
{
"name": "OpenAITelemetryPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
"configSection": "openAITelemetryPlugin"
},
{
"name": "CrudApiPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
"configSection": "feedbackApi",
"urlsToWatch": [
"http://api.ecs.eu/feedback"
]
},
{
"name": "OpenAIMockResponsePlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll"
},
{
"name": "MarkdownReporter",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll"
}
],
"urlsToWatch": [
"https://models.github.ai/inference/chat/completions*"
],
"feedbackApi": {
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v1.0.0/crudapiplugin.schema.json",
"apiFile": "feedback-api.json"
},
"apiLatencyPlugin": {
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v1.0.0/latencyplugin.schema.json",
"minMs": 200,
"maxMs": 500
},
"openAITelemetryPlugin": {
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v1.0.0/openaitelemetryplugin.schema.json",
"currency": "EUR",
"includeCosts": true,
"pricesFile": "azure-ai-prices.json"
},
"languageModel": {
"enabled": true,
"model": "llama3.2"
},
"logLevel": "trace",
"newVersionNotification": "stable",
"showSkipMessages": true
}
85 changes: 85 additions & 0 deletions samples/llm-usage/.github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Dev Proxy LLM Usage Test Project

## Architecture Overview

This is a **Dev Proxy test application** that demonstrates LLM usage monitoring and cost tracking. The app fetches feedback from a mock API, uses OpenAI's GPT-4.1 to categorize feedback, and displays results with visual "pills" - all while Dev Proxy intercepts and analyzes the LLM calls.

**Key Components:**
- **Frontend**: Vanilla JavaScript SPA with OpenAI SDK integration (`js/app.js`)
- **Mock API**: Dev Proxy serves feedback data from `.devproxy/feedback-data.json`
- **LLM Integration**: GitHub Models API (models.github.ai) for GPT-4.1 categorization
- **Testing**: Playwright E2E tests that verify the full AI pipeline
- **Monitoring**: Dev Proxy tracks costs, latency, and usage patterns

## Critical Dev Proxy Integration

**Dev Proxy Configuration** (`.devproxy/devproxyrc.json`):
- `CrudApiPlugin`: Serves mock feedback API from `feedback-data.json`
- `OpenAITelemetryPlugin`: Tracks LLM costs and usage (EUR pricing in `azure-ai-prices.json`)
- `LatencyPlugin`: Simulates 200-500ms API delays
- `MarkdownReporter`: Generates usage reports

**Essential Commands:**
```bash
# Start dev server (port 8007)
npm start

# Run Playwright tests with Dev Proxy
npm test

# Install Edge browser for testing
npm run install:msedge
```

## LLM Integration Pattern

**Authentication**: Uses GitHub token via `js/env.js` (replaced by CI workflow)
**API Configuration**:
```javascript
const llmUrl = "https://models.github.ai/inference";
const model = "openai/gpt-4.1";
```

**Retry Logic**: 3 attempts with category validation (`for-organizers`, `for-speakers`, `useless`)
**UI Pattern**: Pills are hidden by default, revealed via `.pills-visible` class after analysis

## Testing Strategy

**E2E Test Flow** (`tests/feedback-analysis.spec.js`):
1. Load app and wait for feedback items
2. Click "Analyze Feedback" button
3. Wait for "Analysis Complete" (120s timeout for LLM calls)
4. Verify all pills have valid categories (not all "Unknown")

**Key Test Configuration**:
- Uses Microsoft Edge browser only
- Network request/response logging enabled
- Slow test timeout for LLM processing
- Base URL: `http://127.0.0.1:8007`

## CI/CD Integration

**GitHub Actions Workflow** (`.github/workflows/test.yml`):
- Replaces `env.js` with actual `GITHUB_TOKEN`
- Sets up Dev Proxy with auto-recording
- Installs Chromium certificates for proxy
- Uploads Dev Proxy reports and logs as artifacts

**Key Workflow Features**:
- Uses `dev-proxy-tools/actions` for setup
- Enables job summary reporting
- Captures complete proxy logs for debugging

## Development Guidelines

**Environment Setup**: Always use Dev Proxy - the app expects intercepted APIs
**API Mocking**: Modify `.devproxy/feedback-data.json` to change test data
**Styling**: Uses CSS custom properties with dark mode support
**Error Handling**: LLM failures gracefully degrade to "unknown" category

**File Structure**:
- `js/app.js`: Core application logic
- `js/env.js`: API key configuration (replaced in CI)
- `.devproxy/`: Complete Dev Proxy configuration
- `tests/`: Playwright E2E tests
- `css/styles.css`: Modern CSS with pill styling
67 changes: 67 additions & 0 deletions samples/llm-usage/.github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Test Dev Proxy LLM Usage with Playwright

on:
workflow_dispatch:

permissions:
models: read

jobs:
test-llm-usage:
name: Test Dev Proxy LLM Usage with Playwright
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Update env.js
run: |
echo "Updating env.js..."
echo "export const apiKey = '${{ secrets.GITHUB_TOKEN }}';" > ./js/env.js

- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20

- name: Install dependencies
run: npm ci

- name: Install Edge browser
run: npm run install:msedge

- name: Setup Dev Proxy
uses: dev-proxy-tools/actions/setup@v1
with:
auto-record: true
version: v1.0.0-beta.8
report-job-summary: $GITHUB_STEP_SUMMARY

- name: Install the Dev Proxy certificate for Chromium
uses: dev-proxy-tools/actions/chromium-cert@v1

- name: Run Playwright tests
run: npm test

- name: Stop recording
uses: dev-proxy-tools/actions/record-stop@v1

- name: Upload Dev Proxy reports
uses: actions/upload-artifact@v4
with:
name: Reports
path: ./*Reporter*

- name: Show logs
if: always()
run: |
echo "Dev Proxy logs:"
cat devproxy.log

- name: Upload Dev Proxy logs
if: always()
uses: actions/upload-artifact@v4
with:
name: devproxy.log
path: devproxy.log
13 changes: 13 additions & 0 deletions samples/llm-usage/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
node_modules
*.log
.DS_Store
*Reporter*

# Playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/

# act
.artifacts
8 changes: 8 additions & 0 deletions samples/llm-usage/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"recommendations": [
"garrytrinder.dev-proxy-toolkit",
"ms-playwright.playwright",
"github.vscode-github-actions",
"SanjulaGanepola.github-local-actions"
]
}
Loading