Skip to content
DaneshyarDocs
Sign in
  • Introduction
  • Quickstart
  • What is Daneshyar?
  • The Apps page
  • Inside an app
  • Workspace: files and chat
  • Sharing and members
  • Notes and dictation
  • Automations
  • The file reader
  • Your profile
  • API keys
  • Authentication
  • Apps, workspaces, resources
  • Ingestion
  • Chat and sessions
  • Citations
  • Skills
  • Conventions
  • Workspaces
  • Resources
  • Chat
  • Skills
  • Errors
  • Customer support assistant
  • Internal knowledge base
  • Documentation chatbot
  • Academy tutor
  • Embedded widget
  • Widget configuration
  • Framework guides
  • Security
  • Troubleshooting
  • FAQ
  1. Docs
  2. Examples
  3. Academy tutor

Academy tutor

One workspace per course, so students get answers from their own lessons, any hour of the day.

Students ask questions about their course — the slides, handouts and recorded classes — inside your own app. Each answer shows which lesson file it came from.

Architecture

One workspace per course. A student of one course can never get an answer from another course's files, because retrieval never crosses a workspace.

  1. Map each course to a workspace id, and each lesson to its resource ids, in your own config.
  2. Pass the student's id as external_user_id, so each student keeps their own history.
  3. To answer from this week's lesson only, send its ids as resource_ids. Leave it out to search the whole course.

Setup

Create one workspace per course and upload the course files into it: PDFs, slides as images, and class recordings. Wait until each file is ready before students ask about it.

Call the API from your own server, never from the student's browser — the secret key acts as the app owner. If students only need a chat page, the workspace share link or the widget may be enough, with no code.

The code

The course lookup comes first, so an unknown course fails fast instead of searching the wrong files:

JavaScript
const BASE = "https://api.daneshyar.info/api/v1";
const KEY = process.env.DANESHYAR_API_KEY;

/**
 * One workspace per course. A student of Biology 101 never gets an answer
 * from Chemistry 201's notes, because retrieval cannot cross a workspace.
 */
const COURSES = {
  "biology-101": "8f14e45f-ceea-467a-9f4c-1a2b3c4d5e6f",
  "chemistry-201": "1d4c7b90-3e58-4a21-bf06-9c2e5d8a7f43",
};

/**
 * Keep each lesson's resource ids in your own config. Pass them as
 * resource_ids to answer from this week's lesson only.
 */
const LESSONS = {
  "biology-101": {
    "week-3": ["3c9a1b77-0d24-4e8b-b0f1-7a5e9c2d4b81"],
  },
};

/** Called from your own /tutor/ask route, which holds the key. */
export async function askTutor({ studentId, course, lesson, question, sessionId }) {
  const workspaceId = COURSES[course];
  if (!workspaceId) throw new Error(`Unknown course: ${course}`);

  const res = await fetch(`${BASE}/workspaces/${workspaceId}/chat/`, {
    method: "POST",
    headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
    body: JSON.stringify({
      // Your student id, so each student keeps their own history.
      external_user_id: studentId,
      session_id: sessionId,
      message: question,
      // Leave it out to search the whole course.
      resource_ids: LESSONS[course]?.[lesson],
    }),
  });

  if (!res.ok) {
    const body = await res.json().catch(() => ({}));
    throw new Error(body.message ?? body.detail ?? `HTTP ${res.status}`);
  }

  const { data } = await res.json();
  const reply = data.assistant_message;

  return {
    sessionId: data.session.id,
    answer: reply.content,
    // Show the student which lesson file the answer came from.
    sources: [
      ...new Map(
        reply.citations.map((c) => [c.resource_id, c.resource_title]),
      ),
    ].map(([id, title]) => ({ id, title })),
  };
}

What you get

Every student gets answers from exactly their course's files, with the lesson named under each answer and their own history. Adding a course is one workspace and one config line.

PreviousDocumentation chatbotNextEmbedded widget

On this page

  • Architecture
  • Setup
  • The code
  • What you get

Every example on this site runs against the live API. If one doesn't, tell us — that's a bug in the docs.

ServicesAboutTalk to us