Jacek Zadrożny Claude Sonnet 4.5 commited on
Commit
68287f9
·
1 Parent(s): 86d06c6

Rebrand do Jacek AI + nowy interfejs Q&A + social sharing

Browse files

GŁÓWNE ZMIANY:
- Zmiana nazwy z "A11y Expert" na "Jacek AI" (wszędzie)
- Przeprojektowanie interfejsu z chatbota na formularz Q&A
- Naprawiono system prompty (usunięto mylące instrukcje o narzędziach)
- Dodano przyciski social media i kopiowania

INTERFEJS:
- Usunięto chat bubbles, zastąpiono pojedynczym polem Q&A
- Zmieniono ikonę z ♿ na 🎯
- Usunięto przykładowe pytania
- Dodano duże pole textarea (5 linii) na pytania
- Przycisk "Zapytaj eksperta" zamiast "Wyślij"

FUNKCJE:
- 📋 Przycisk kopiowania odpowiedzi (bez alertu)
- 🐦 Twitter/X sharing (pełny tekst + link)
- 💼 LinkedIn sharing (tylko URL - ograniczenie API)
- 📘 Facebook sharing (tylko URL - ograniczenie API)
- Open Graph meta tagi dla lepszych preview

KONFIGURACJA:
- Dodano PUBLIC_URL do config.py
- Domyślny URL: https://huggingface.co/spaces/jaczad/JacekAI
- Wsparcie dla zmiennej środowiskowej OPEN_API_KEY (alias)

POPRAWKI PROMPTÓW:
- Usunięto mylące "DOSTĘPNE NARZĘDZIA" z system promptu
- Agent odpowiada bezpośrednio zamiast "przeszukiwania bazy"
- Wzmocniono wymuszanie języka polskiego

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

Files changed (6) hide show
  1. .env.example +3 -0
  2. CLAUDE.md +1 -1
  3. README.md +2 -2
  4. agent/prompts.py +10 -20
  5. app.py +145 -62
  6. config.py +6 -1
.env.example CHANGED
@@ -22,3 +22,6 @@ LOG_LEVEL=INFO
22
  # UI Configuration (for Huggingface Spaces use 0.0.0.0:7860)
23
  SERVER_HOST=0.0.0.0
24
  SERVER_PORT=7860
 
 
 
 
22
  # UI Configuration (for Huggingface Spaces use 0.0.0.0:7860)
23
  SERVER_HOST=0.0.0.0
24
  SERVER_PORT=7860
25
+
26
+ # Public URL for social media sharing
27
+ PUBLIC_URL=https://huggingface.co/spaces/jaczad/JacekAI
CLAUDE.md CHANGED
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
4
 
5
  ## Project Overview
6
 
7
- **A11y Expert** is a bilingual (Polish/English) accessibility chatbot built with Gradio, utilizing RAG (Retrieval-Augmented Generation) to answer questions about digital accessibility standards (WCAG 2.2, WAI-ARIA). The agent uses OpenAI GPT-4 with a LanceDB vector database containing accessibility knowledge.
8
 
9
  ## Quick Start Commands
10
 
 
4
 
5
  ## Project Overview
6
 
7
+ **Jacek AI** is a bilingual (Polish/English) accessibility chatbot built with Gradio, utilizing RAG (Retrieval-Augmented Generation) to answer questions about digital accessibility standards (WCAG 2.2, WAI-ARIA). The agent uses OpenAI GPT-4 with a LanceDB vector database containing accessibility knowledge.
8
 
9
  ## Quick Start Commands
10
 
README.md CHANGED
@@ -1,5 +1,5 @@
1
  ---
2
- title: JacekAI - A11y Expert
3
  emoji: ♿
4
  colorFrom: blue
5
  colorTo: green
@@ -11,7 +11,7 @@ pinned: true
11
  short_description: Inteligentny asystent do spraw dostępności cyfrowej
12
  ---
13
 
14
- # 🤖 A11y Expert - Asystent Dostępności Cyfrowej
15
 
16
  Inteligentny agent AI wyspecjalizowany w dostępności cyfrowej (a11y), wykorzystujący RAG (Retrieval-Augmented Generation) z bazą wiedzy WCAG, ARIA i najlepszych praktyk.
17
 
 
1
  ---
2
+ title: Jacek AI - Ekspert Dostępności
3
  emoji: ♿
4
  colorFrom: blue
5
  colorTo: green
 
11
  short_description: Inteligentny asystent do spraw dostępności cyfrowej
12
  ---
13
 
14
+ # 🤖 Jacek AI - Asystent Dostępności Cyfrowej
15
 
16
  Inteligentny agent AI wyspecjalizowany w dostępności cyfrowej (a11y), wykorzystujący RAG (Retrieval-Augmented Generation) z bazą wiedzy WCAG, ARIA i najlepszych praktyk.
17
 
agent/prompts.py CHANGED
@@ -26,16 +26,11 @@ OBOWIĄZKOWE ZASADY:
26
  6. ✅ Podawaj praktyczne przykłady kodu lub implementacji
27
  7. ✅ Wspominaj zarówno wymagania WCAG jak i best practices
28
 
29
- DOSTĘPNE NARZĘDZIA:
30
- - search_knowledge_base: Wyszukaj w bazie wiedzy o dostępności (zawsze używaj przed odpowiedzią!)
31
- - get_database_stats: Sprawdź statystyki bazy wiedzy
32
-
33
- STRUKTURA ODPOWIEDZI:
34
- 1. Przeszukaj bazę wiedzy (użyj narzędzia search_knowledge_base)
35
- 2. Zacytuj dokładne kryteria WCAG lub specyfikacje
36
- 3. Wyjaśnij w prostych słowach
37
- 4. Podaj przykłady (HTML, ARIA atrybuty, testy)
38
- 5. Podaj źródła
39
  """
40
 
41
  SYSTEM_PROMPT_EN = """
@@ -56,16 +51,11 @@ MANDATORY RULES:
56
  6. ✅ Provide practical code examples or implementation guidance
57
  7. ✅ Reference both WCAG requirements and industry best practices
58
 
59
- AVAILABLE TOOLS:
60
- - search_knowledge_base: Search the accessibility knowledge base (always use before answering!)
61
- - get_database_stats: Check knowledge base statistics
62
-
63
- RESPONSE STRUCTURE:
64
- 1. Search the knowledge base (use search_knowledge_base tool)
65
- 2. Quote exact WCAG criteria or specifications
66
- 3. Explain in simple terms
67
- 4. Provide examples (HTML, ARIA attributes, testing approaches)
68
- 5. Provide sources
69
  """
70
 
71
  SYSTEM_PROMPT_WCAG_EXPERT = """
 
26
  6. ✅ Podawaj praktyczne przykłady kodu lub implementacji
27
  7. ✅ Wspominaj zarówno wymagania WCAG jak i best practices
28
 
29
+ JAK DZIAŁASZ:
30
+ - Dostajesz kontekst z bazy wiedzy WCAG/ARIA (już przeszukanej automatycznie)
31
+ - Używasz tego kontekstu do udzielenia szczegółowej odpowiedzi
32
+ - Odpowiadasz BEZPOŚREDNIO na pytanie (bez mówienia o przeszukiwaniu bazy!)
33
+ - Skupiasz się na merytoryce, nie na procesie
 
 
 
 
 
34
  """
35
 
36
  SYSTEM_PROMPT_EN = """
 
51
  6. ✅ Provide practical code examples or implementation guidance
52
  7. ✅ Reference both WCAG requirements and industry best practices
53
 
54
+ HOW YOU WORK:
55
+ - You receive context from the WCAG/ARIA knowledge base (already searched automatically)
56
+ - You use this context to provide detailed answers
57
+ - You respond DIRECTLY to the question (don't talk about searching the database!)
58
+ - Focus on the content, not the process
 
 
 
 
 
59
  """
60
 
61
  SYSTEM_PROMPT_WCAG_EXPERT = """
app.py CHANGED
@@ -1,6 +1,6 @@
1
  """
2
- Gradio UI for the A11y Expert Agent with lazy initialization.
3
- This module creates a Gradio ChatInterface that starts FAST,
4
  then initializes the agent in the background.
5
  """
6
  import sys
@@ -74,14 +74,14 @@ def cleanup_resources():
74
  logger.warning(f"Error during cleanup: {e}")
75
 
76
  # --- Gradio Chat Logic ---
77
- def respond(message: str, history: list[list[str]]):
78
  """
79
- Main function for the Gradio ChatInterface.
80
- Receives a user message and chat history, then uses the agent
81
- to generate a streaming response.
82
  Args:
83
- message: The user's input message.
84
- history: The conversation history provided by Gradio.
85
  Yields:
86
  A stream of response chunks to update the UI.
87
  """
@@ -104,7 +104,7 @@ def respond(message: str, history: list[list[str]]):
104
  logger.info("🔄 Background init not complete, initializing synchronously...")
105
  agent_instance = create_agent()
106
  agent_ready = True
107
- logger.success("✅ A11y Expert Agent is ready!")
108
  except Exception as e:
109
  logger.error(f"❌ Failed to initialize agent: {e}")
110
  import traceback
@@ -135,39 +135,62 @@ def respond(message: str, history: list[list[str]]):
135
 
136
 
137
  # --- Gradio UI Definition ---
138
- # Two-column layout: Chat on left, Markdown content on right
139
- with gr.Blocks(title="A11y Expert") as demo:
140
- gr.Markdown("# 🤖 A11y Expert")
141
- gr.Markdown("Twój inteligentny asystent do spraw dostępności cyfrowej.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  gr.Markdown("ℹ️ *Pytania i odpowiedzi są zapisywane anonimowo w celu poprawy systemu i budowy zestawu danych treningowych.*")
143
 
144
  with gr.Row():
145
- # Left column: Chatbot
146
  with gr.Column(scale=1):
147
- gr.Markdown("### 💬 Chat")
148
- chatbot = gr.Chatbot(height=500, show_label=False)
149
- msg = gr.Textbox(
150
- placeholder="Zadaj pytanie o WCAG, ARIA, lub poproś o analizę kodu...",
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  show_label=False,
152
- container=False,
153
- max_length=300
 
 
 
154
  )
155
-
 
156
  with gr.Row():
157
- submit = gr.Button("Wyślij", variant="primary")
158
- clear = gr.Button("Wyczyść")
159
-
160
- # Example questions
161
- gr.Examples(
162
- examples=[
163
- "Jakie są wymagania WCAG 2.2 dla etykiet formularzy?",
164
- "Wyjaśnij rolę 'alert' w ARIA i podaj przykład.",
165
- "Czy ten przycisk jest dostępny? <div onclick='...'>Click me</div>",
166
- "Jaka jest różnica między aria-label a aria-labelledby?",
167
- ],
168
- inputs=msg,
169
- label="Przykładowe pytania"
170
- )
171
 
172
  # Right column: Markdown content from file
173
  with gr.Column(scale=1):
@@ -180,7 +203,7 @@ with gr.Blocks(title="A11y Expert") as demo:
180
  return f.read()
181
  except FileNotFoundError:
182
  return """
183
- ## Witaj w A11y Expert! 👋
184
 
185
  Stwórz plik `notes.md` w katalogu projektu aby zobaczyć tutaj swoje notatki.
186
 
@@ -204,34 +227,94 @@ Stwórz plik `notes.md` w katalogu projektu aby zobaczyć tutaj swoje notatki.
204
  outputs=markdown_content
205
  )
206
 
207
- # Chat logic
208
- def user_message(user_input, history):
209
- """Add user message to chat history."""
210
- return "", history + [{"role": "user", "content": user_input}]
211
-
212
- def bot_response(history):
213
- """Generate bot response."""
214
- user_input = history[-1]["content"]
215
-
216
- # Extract text from multimodal format if needed
217
- if isinstance(user_input, list):
218
- user_input = " ".join([item.get("text", "") for item in user_input if item.get("type") == "text"])
219
-
220
- # Add assistant message placeholder
221
- history.append({"role": "assistant", "content": ""})
222
-
223
- for response in respond(user_input, history[:-1]):
224
- history[-1]["content"] = response
225
- yield history
226
-
227
- # Wire up the chat
228
- submit.click(user_message, [msg, chatbot], [msg, chatbot], queue=False).then(
229
- bot_response, chatbot, chatbot
 
230
  )
231
- msg.submit(user_message, [msg, chatbot], [msg, chatbot], queue=False).then(
232
- bot_response, chatbot, chatbot
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  )
234
- clear.click(lambda: None, None, chatbot, queue=False)
235
 
236
 
237
  # --- App Launch ---
 
1
  """
2
+ Gradio UI for Jacek AI with lazy initialization.
3
+ This module creates a Gradio interface that starts FAST,
4
  then initializes the agent in the background.
5
  """
6
  import sys
 
74
  logger.warning(f"Error during cleanup: {e}")
75
 
76
  # --- Gradio Chat Logic ---
77
+ def respond(message: str):
78
  """
79
+ Main function for the Gradio Q&A interface.
80
+ Receives a user question and uses the agent to generate a streaming response.
81
+
82
  Args:
83
+ message: The user's question.
84
+
85
  Yields:
86
  A stream of response chunks to update the UI.
87
  """
 
104
  logger.info("🔄 Background init not complete, initializing synchronously...")
105
  agent_instance = create_agent()
106
  agent_ready = True
107
+ logger.success("✅ Jacek AI Agent is ready!")
108
  except Exception as e:
109
  logger.error(f"❌ Failed to initialize agent: {e}")
110
  import traceback
 
135
 
136
 
137
  # --- Gradio UI Definition ---
138
+ # Q&A Form layout: Question form on left, Notes on right
139
+ # Get public URL from config for social sharing
140
+ SHARE_URL = get_settings().public_url
141
+
142
+ # Custom HTML head for Open Graph meta tags (for LinkedIn/Facebook previews)
143
+ custom_head = """
144
+ <meta property="og:title" content="Jacek AI - Ekspert Dostępności Cyfrowej" />
145
+ <meta property="og:description" content="Uzyskaj odpowiedzi na pytania o WCAG, ARIA i najlepsze praktyki dostępności cyfrowej. Chatbot oparty na sztucznej inteligencji." />
146
+ <meta property="og:type" content="website" />
147
+ <meta property="og:url" content="https://huggingface.co/spaces/jaczad/JacekAI" />
148
+ <meta name="twitter:card" content="summary_large_image" />
149
+ <meta name="twitter:title" content="Jacek AI - Ekspert Dostępności" />
150
+ <meta name="twitter:description" content="Chatbot AI odpowiadający na pytania o dostępność cyfrową (WCAG, ARIA)" />
151
+ """
152
+
153
+ with gr.Blocks(title="Jacek AI", head=custom_head) as demo:
154
+ gr.Markdown("# 🎯 Jacek AI")
155
+ gr.Markdown("**Ekspert dostępności cyfrowej** - Uzyskaj odpowiedzi na pytania o WCAG, ARIA i najlepsze praktyki.")
156
  gr.Markdown("ℹ️ *Pytania i odpowiedzi są zapisywane anonimowo w celu poprawy systemu i budowy zestawu danych treningowych.*")
157
 
158
  with gr.Row():
159
+ # Left column: Q&A Form
160
  with gr.Column(scale=1):
161
+ gr.Markdown("### 📋 Zadaj pytanie ekspertowi")
162
+
163
+ question_input = gr.Textbox(
164
+ label="Twoje pytanie lub problem:",
165
+ placeholder="Opisz problem z dostępnością, zapytaj o wymagania WCAG, poproś o przegląd kodu...\n\nPrzykład: Jakie są wymagania kontrastu dla tekstu według WCAG 2.2?",
166
+ lines=5,
167
+ max_length=1000,
168
+ show_label=True
169
+ )
170
+
171
+ submit_btn = gr.Button("🔍 Zapytaj eksperta", variant="primary", size="lg")
172
+
173
+ gr.Markdown("---")
174
+ gr.Markdown("### 📊 Odpowiedź eksperta:")
175
+
176
+ answer_output = gr.Textbox(
177
+ value="*Odpowiedź pojawi się tutaj po zadaniu pytania...*",
178
  show_label=False,
179
+ elem_id="answer_output",
180
+ lines=15,
181
+ max_lines=30,
182
+ interactive=False,
183
+ container=False
184
  )
185
+
186
+ # Action buttons
187
  with gr.Row():
188
+ copy_btn = gr.Button("📋 Kopiuj odpowiedź", variant="secondary", size="sm")
189
+ share_twitter = gr.Button("🐦 X/Twitter", variant="secondary", size="sm")
190
+ share_linkedin = gr.Button("💼 LinkedIn", variant="secondary", size="sm")
191
+ share_facebook = gr.Button("📘 Facebook", variant="secondary", size="sm")
192
+
193
+ gr.Markdown("---")
 
 
 
 
 
 
 
 
194
 
195
  # Right column: Markdown content from file
196
  with gr.Column(scale=1):
 
203
  return f.read()
204
  except FileNotFoundError:
205
  return """
206
+ ## Witaj w Jacek AI! 👋
207
 
208
  Stwórz plik `notes.md` w katalogu projektu aby zobaczyć tutaj swoje notatki.
209
 
 
227
  outputs=markdown_content
228
  )
229
 
230
+ # Q&A logic
231
+ def handle_question(question):
232
+ """Process question and return answer."""
233
+ if not question or not question.strip():
234
+ return "⚠️ Proszę wpisać pytanie."
235
+
236
+ # Show loading message
237
+ yield "⏳ Analizuję pytanie i przeszukuję bazę wiedzy..."
238
+
239
+ # Generate answer (streaming)
240
+ for chunk in respond(question):
241
+ yield chunk
242
+
243
+ # Wire up the Q&A form
244
+ submit_btn.click(
245
+ fn=handle_question,
246
+ inputs=question_input,
247
+ outputs=answer_output
248
+ )
249
+
250
+ question_input.submit(
251
+ fn=handle_question,
252
+ inputs=question_input,
253
+ outputs=answer_output
254
  )
255
+
256
+ # Copy button - copies the answer text (no alert)
257
+ copy_btn.click(
258
+ fn=None,
259
+ inputs=answer_output,
260
+ outputs=None,
261
+ js="""
262
+ (answer) => {
263
+ navigator.clipboard.writeText(answer);
264
+ }
265
+ """
266
+ )
267
+
268
+ # Social media share buttons
269
+ share_twitter.click(
270
+ fn=None,
271
+ inputs=[question_input, answer_output],
272
+ outputs=None,
273
+ js=f"""
274
+ (question, answer) => {{
275
+ // Prepare tweet with full question and answer (preserve paragraph breaks)
276
+ const cleanAnswer = answer
277
+ .replace(/#+\\s*/g, '') // Remove markdown headers
278
+ .replace(/\\n{{3,}}/g, '\\n\\n') // Multiple newlines -> double newline
279
+ .replace(/\\*\\*/g, '') // Remove bold markdown
280
+ .trim();
281
+
282
+ const text = `Zapytałem Jacek AI: "${{question}}"\\n\\n${{cleanAnswer}}\\n\\n🔗 `;
283
+ const url = '{SHARE_URL}';
284
+
285
+ // Twitter automatically appends URL, but we add it in text too for clarity
286
+ window.open(`https://twitter.com/intent/tweet?text=${{encodeURIComponent(text + url)}}`, '_blank');
287
+ }}
288
+ """
289
+ )
290
+
291
+ share_linkedin.click(
292
+ fn=None,
293
+ inputs=[question_input, answer_output],
294
+ outputs=None,
295
+ js=f"""
296
+ (question, answer) => {{
297
+ const url = '{SHARE_URL}';
298
+ // LinkedIn API limitation: cannot pre-fill post text (security policy)
299
+ // Only URL is shared - LinkedIn scrapes Open Graph meta tags for preview
300
+ window.open(`https://www.linkedin.com/sharing/share-offsite/?url=${{encodeURIComponent(url)}}`, '_blank');
301
+ }}
302
+ """
303
+ )
304
+
305
+ share_facebook.click(
306
+ fn=None,
307
+ inputs=[question_input, answer_output],
308
+ outputs=None,
309
+ js=f"""
310
+ (question, answer) => {{
311
+ const url = '{SHARE_URL}';
312
+ // Facebook API limitation: cannot pre-fill post text (security policy)
313
+ // Only URL is shared - Facebook scrapes Open Graph meta tags for preview
314
+ window.open(`https://www.facebook.com/sharer/sharer.php?u=${{encodeURIComponent(url)}}`, '_blank');
315
+ }}
316
+ """
317
  )
 
318
 
319
 
320
  # --- App Launch ---
config.py CHANGED
@@ -41,7 +41,8 @@ class Settings(BaseSettings):
41
  # API Configuration (required)
42
  openai_api_key: str = Field(
43
  default="",
44
- description="OpenAI API key - required for LLM and embeddings"
 
45
  )
46
 
47
  # LLM Configuration
@@ -101,6 +102,10 @@ class Settings(BaseSettings):
101
  le=65535,
102
  description="Gradio server port"
103
  )
 
 
 
 
104
 
105
  @field_validator("openai_api_key")
106
  @classmethod
 
41
  # API Configuration (required)
42
  openai_api_key: str = Field(
43
  default="",
44
+ description="OpenAI API key - required for LLM and embeddings",
45
+ validation_alias="OPEN_API_KEY" # Also accept OPEN_API_KEY (common typo)
46
  )
47
 
48
  # LLM Configuration
 
102
  le=65535,
103
  description="Gradio server port"
104
  )
105
+ public_url: str = Field(
106
+ default="https://huggingface.co/spaces/jaczad/JacekAI",
107
+ description="Public URL for social media sharing"
108
+ )
109
 
110
  @field_validator("openai_api_key")
111
  @classmethod