Skip to content

Code Generation from Documentation

This example learns a library's API from documentation text and generates worked code examples for a set of use cases. It demonstrates list-valued output fields, composing several ChainOfThought predictors, and threading their outputs through a plain Scala class.

Signatures with list-valued fields

trait LibraryAnalyzer extends Spec:
  def library_name: InputField[String]
  def documentation_content: InputField[String]
  def core_concepts: OutputField[List[String]]
  def common_patterns: OutputField[List[String]]
  def key_methods: OutputField[List[String]]
  def installation_info: OutputField[String]
  def code_examples: OutputField[List[String]]

A Spec trait declares the input and output fields. An OutputField[List[String]] decodes to List[String], so a single predictor call returns several structured lists at once. The example defines three such specs: one to analyze documentation, one to generate code for a use case, and one to refine code given feedback.

Composing ChainOfThought predictors

final class DocumentationLearningAgent:
  private val analyzeDocs  = ChainOfThought(Signature.of[LibraryAnalyzer])
  private val generateCode = ChainOfThought(Signature.of[CodeGenerator])
  private val refineCode   = ChainOfThought(Signature.of[RefineCode])

DocumentationLearningAgent holds one ChainOfThought predictor per signature, built from Signature.of[T]. Each predictor is a field on the class; the agent's methods call them and map their outputs into the LibraryInfo and GeneratedExample case classes.

Threading outputs

def learnAndGenerate(
    libraryName    : String,
    combinedContent: String,
    useCases       : Vector[String] = defaultUseCases
)(using RuntimeContext): Either[DspyError, (LibraryInfo, Vector[GeneratedExample])] =
  val agent = new DocumentationLearningAgent
  for
    info     <- agent.learnFromDocs(libraryName, combinedContent)
    examples <- useCases.foldLeft[Either[DspyError, Vector[GeneratedExample]]](Right(Vector.empty)) {
                  (acc, useCase) =>
                    for
                      sofar <- acc
                      ex    <- agent.generateExample(
                              info,
                              useCase,
                              requirements = "Include error handling, comments, and best practices"
                            )
                    yield sofar :+ ex
                }
  yield (info, examples)

learnAndGenerate runs the full flow inside an Either for comprehension. It first analyzes the combined documentation into a LibraryInfo, then folds over the use cases, generating one GeneratedExample per case and accumulating them in a Vector. Any DspyError short-circuits the comprehension.

Running it

OPENAI_API_KEY=sk-... sbt "examples/runMain dspy4s.examples.tutorials.sample_code_generation.sampleCodeGenerationMain"

Notes

Out of scope: fetching documentation over HTTP, the interactive console session, and saving results to JSON. learnFromDocs takes already-combined documentation text as input instead of fetching it.

Full source: SampleCodeGeneration.scala